using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using Community.CsharpSqlite.Entity;

namespace Community.CsharpSqlite
{
    public  class Build
    {
        /*
    ** 2001 September 15
    **
    ** The author disclaims copyright to this source code.  In place of
    ** a legal notice, here is a blessing:
    **
    **    May you do good and not evil.
    **    May you find forgiveness for yourself and forgive others.
    **    May you share freely, never taking more than you give.
    **
    *************************************************************************
    ** This file contains C code routines that are called by the SQLite parser
    ** when syntax rules are reduced.  The routines in this file handle the
    ** following kinds of SQL syntax:
    **
    **     CREATE TABLE
    **     DROP TABLE
    **     CREATE INDEX
    **     DROP INDEX
    **     creating ID lists
    **     BEGIN TRANSACTION
    **     COMMIT
    **     ROLLBACK
    *************************************************************************
    **  Included in SQLite3 port to C#-SQLite;  2008 Noah B Hart
    **  C#-SQLite is an independent reimplementation of the SQLite software library
    **
    **  SQLITE_SOURCE_ID: 2010-03-09 19:31:43 4ae453ea7be69018d8c16eb8dabe05617397dc4d
    **
    **  $Header: Community.CsharpSqlite/src/build_c.cs,v 6604176a7dbe 2010/03/12 23:35:36 Noah $
    *************************************************************************
    */
        //#include "sqliteInt.h"

        /*
    ** This routine is called when a new SQL statement is beginning to
    ** be parsed.  Initialize the pParse structure as needed.
    */

        public static void BeginParse(Parse pParse, int explainFlag)
        {
            pParse.explain = (byte) explainFlag;
            pParse.nVar = 0;
        }

#if !SQLITE_OMIT_SHARED_CACHE
/*
** The TableLock structure is only used by the sqlite3TableLock() and
** codeTableLocks() functions.
*/
//struct TableLock {
//  int iDb;             /* The database containing the table to be locked */
//  int iTab;            /* The root page of the table to be locked */
//  byte isWriteLock;      /* True for write lock.  False for a read lock */
//  string zName;   /* Name of the table */
//};

public class TableLock
{
public int iDb;         /* The database containing the table to be locked */
public int iTab;        /* The root page of the table to be locked */
public byte isWriteLock;  /* True for write lock.  False for a read lock */
public string zName;    /* Name of the table */
}
/*
** Record the fact that we want to lock a table at run-time.
**
** The table to be locked has root page iTab and is found in database iDb.
** A read or a write lock can be taken depending on isWritelock.
**
** This routine just records the fact that the lock is desired.  The
** code to make the lock occur is generated by a later call to
** codeTableLocks() which occurs during Build.FinishCoding().
*/
void sqlite3TableLock(
  Parse *pParse,     /* Parsing context */
  int iDb,           /* Index of the database containing the table to lock */
  int iTab,          /* Root page number of the table to be locked */
  byte isWriteLock,    /* True for a write lock */
  const char *zName  /* Name of the table to be locked */
){
  Parse *pToplevel = sqlite3ParseToplevel(pParse);
  int i;
  int nBytes;
  TableLock *p;
  assert( iDb>=0 );

  for(i=0; i<pToplevel->nTableLock; i++){
    p = &pToplevel->aTableLock[i];
    if( p->iDb==iDb && p->iTab==iTab ){
      p->isWriteLock = (p->isWriteLock || isWriteLock);
      return;
    }
  }

  nBytes = sizeof(TableLock) * (pToplevel->nTableLock+1);
  pToplevel->aTableLock =
      Malloc.DbReallocOrFree(pToplevel->db, pToplevel->aTableLock, nBytes);
  if( pToplevel->aTableLock ){
    p = &pToplevel->aTableLock[pToplevel->nTableLock++];
    p->iDb = iDb;
    p->iTab = iTab;
    p->isWriteLock = isWriteLock;
    p->zName = zName;
  }else{
    pToplevel->nTableLock = 0;
    pToplevel->db->mallocFailed = 1;
  }
}

/*
** Code an OPCode.OP_TableLock instruction for each table locked by the
** statement (configured by calls to sqlite3TableLock()).
*/
static void codeTableLocks( Parse pParse )
{
int i;
Vdbe pVdbe;

pVdbe = SelectHelper.GetVdbe( pParse );
Debug.Assert( pVdbe != null ); /* SelectHelper.GetVdbe cannot fail: VDBE already allocated */

for ( i = 0 ; i < pParse.nTableLock ; i++ )
{
TableLock p = pParse.aTableLock[i];
int p1 = p.iDb;
VdbeAux.VdbeAddOp4( pVdbe, OPCode.OP_TableLock, p1, p.iTab, p.isWriteLock,
p.zName, P4Type.P4_STATIC );
}
}
#else
        //  #define codeTableLocks(x)
        private static void codeTableLocks(Parse pParse)
        {
        }
#endif

        /*
** This routine is called after a single SQL statement has been
** parsed and a VDBE program to execute that statement has been
** prepared.  This routine puts the finishing touches on the
** VDBE program and resets the pParse structure for the next
** parse.
**
** Note that if an error occurred, it might be the case that
** no VDBE code was generated.
*/

        public static void FinishCoding(Parse pParse)
        {
            sqlite3 db;
            Vdbe v;

            db = pParse.db;
            //      if ( db.mallocFailed != 0 ) return;
            if (pParse.nested != 0) return;
            if (pParse.nErr != 0) return;

            /* Begin by generating some termination code at the end of the
      ** vdbe program
      */
            v = SelectHelper.GetVdbe(pParse);
            Debug.Assert(0 == pParse.isMultiWrite
                         || VdbeAux.VdbeAssertMayAbort(v, pParse.mayAbort) != 0);
            if (v != null)
            {
                VdbeAux.VdbeAddOp0(v, OPCode.OP_Halt);

                /* The cookie mask contains one bit for each database file open.
        ** (Bit 0 is for main, bit 1 is for temp, and so forth.)  Bits are
        ** set for each database that is used.  Generate code to start a
        ** transaction on each used database and to verify the schema cookie
        ** on each used database.
        */
                if (pParse.cookieGoto > 0)
                {
                    uint mask;
                    int iDb;
                    VdbeAux.VdbeJumpHere(v, pParse.cookieGoto - 1);
                    for (iDb = 0, mask = 1; iDb < db.nDb; mask <<= 1, iDb++)
                    {
                        if ((mask & pParse.cookieMask) == 0) continue;
                        VdbeAux.VdbeUsesBtree(v, iDb);
                        VdbeAux.VdbeAddOp2(v, OPCode.OP_Transaction, iDb, (mask & pParse.writeMask) != 0);
                        if (db.init.busy == 0)
                        {
                            VdbeAux.VdbeAddOp2(v, OPCode.OP_VerifyCookie, iDb, pParse.cookieValue[iDb]);
                        }
                    }
#if !SQLITE_OMIT_VIRTUALTABLE
{
int i;
for(i=0; i<pParse.nVtabLock; i++){
char *vtab = (char *)sqlite3GetVTable(db, pParse->apVtabLock[i]);
VdbeAux.VdbeAddOp4(v, OPCode.OP_VBegin, 0, 0, 0, vtab, P4Type.P4_VTAB);
}
pParse.nVtabLock = 0;
}
#endif

                    /* Once all the cookies have been verified and transactions opened,
** obtain the required table-locks. This is a no-op unless the
** shared-cache feature is enabled.
*/
                    codeTableLocks(pParse);

                    /* Initialize any AUTOINCREMENT data structures required.
          */
                    sqlite3AutoincrementBegin(pParse);

                    /* Finally, jump back to the beginning of the executable code. */
                    VdbeAux.VdbeAddOp2(v, OPCode.OP_Goto, 0, pParse.cookieGoto);
                }
            }


            /* Get the VDBE program ready for execution
      */
            if (v != null && UnitTest.ALWAYS(pParse.nErr == 0) /* && 0 == db.mallocFailed */)
            {
#if  SQLITE_DEBUG
                TextWriter trace = (db.flags & Flag.SQLITE_VdbeTrace) != 0 ? Console.Out : null;
                VdbeAux.VdbeTrace(v, trace);
#endif
                Debug.Assert(pParse.iCacheLevel == 0); /* Disables and re-enables match */
                /* A minimum of one cursor is required if autoincrement is used
        *  See ticket [a696379c1f08866] */
                if (pParse.pAinc != null && pParse.nTab == 0) pParse.nTab = 1;
                VdbeAux.VdbeMakeReady(v, pParse.nVar, pParse.nMem,
                                     pParse.nTab, pParse.nMaxArg, pParse.explain,
                                     (pParse.isMultiWrite != 0 && pParse.mayAbort != 0) ? 1 : 0);
                pParse.rc = StatusCode.SQLITE_DONE;
                pParse.colNamesSet = 0;
            }
            else
            {
                pParse.rc = StatusCode.SQLITE_ERROR;
            }
            pParse.nTab = 0;
            pParse.nMem = 0;
            pParse.nSet = 0;
            pParse.nVar = 0;
            pParse.cookieMask = 0;
            pParse.cookieGoto = 0;
        }

        /*
    ** Run the parser and code generator recursively in order to generate
    ** code for the SQL statement given onto the end of the pParse context
    ** currently under construction.  When the parser is run recursively
    ** this way, the final OPCode.OP_Halt is not appended and other initialization
    ** and finalization steps are omitted because those are handling by the
    ** outermost parser.
    **
    ** Not everything is nestable.  This facility is designed to permit
    ** INSERT, UPDATE, and DELETE operations against SQLITE_MASTER.  Use
    ** care if you decide to try to use this routine for some other purposes.
    */

        public static void NestedParse(Parse pParse, string zFormat, params object[] ap)
        {
            //  va_list ap;
            string zSql; //  char *zSql;
            string zErrMsg = ""; //  char* zErrMsg = 0;
            sqlite3 db = pParse.db;
            //# define SAVE_SZ  (Parse.Length - offsetof(Parse,nVar))
            //  char saveBuf[SAVE_SZ];

            if (pParse.nErr != 0) return;
            Debug.Assert(pParse.nested < 10); /* Nesting should only be of limited depth */
            Custom.VaStart(ap, zFormat);
            zSql = Print.VMPrintf(db, zFormat, ap);
            Custom.VaEnd(ap);
            //if( zSql=="" ){
            //  return;   /* A malloc must have failed */
            //}
            pParse.nested++;
            pParse.SaveMembers(); //  memcpy(saveBuf, pParse.nVar, SAVE_SZ);
            pParse.ResetMembers(); //  memset(pParse.nVar, 0, SAVE_SZ);
            Tokenize.RunParser(pParse, zSql, ref zErrMsg);
            MemPool.DbFree(db, ref zErrMsg);
            MemPool.DbFree(db, ref zSql);
            pParse.RestoreMembers(); //  memcpy(pParse.nVar, saveBuf, SAVE_SZ);
            pParse.nested--;
        }

        /*
    ** Locate the in-memory structure that describes a particular database
    ** table given the name of that table and (optionally) the name of the
    ** database containing the table.  Return NULL if not found.
    **
    ** If zDatabase is 0, all databases are searched for the table and the
    ** first matching table is returned.  (No checking for duplicate table
    ** names is done.)  The search order is TEMP first, then MAIN, then any
    ** auxiliary databases added using the ATTACH command.
    **
    ** See also Build.LocateTable().
    */

        public static Table FindTable(sqlite3 db, string zName, string zDatabase)
        {
            Table p = null;
            int i;
            int nName;
            Debug.Assert(zName != null);
            nName = Utility.Sqlite3Strlen30(zName);
            for (i = Const.OMIT_TEMPDB; i < db.nDb; i++)
            {
                int j = (i < 2) ? i ^ 1 : i; /* Search TEMP before MAIN */
                if (zDatabase != null && Utility.Sqlite3StrICmp(zDatabase, db.aDb[j].zName) != 0) continue;
                p = (Table) HashHelper.HashFind(db.aDb[j].pSchema.tblHash, zName, nName);
                if (p != null) break;
            }
            return p;
        }

        /*
    ** Locate the in-memory structure that describes a particular database
    ** table given the name of that table and (optionally) the name of the
    ** database containing the table.  Return NULL if not found.  Also leave an
    ** error message in pParse.zErrMsg.
    **
    ** The difference between this routine and Build.FindTable() is that this
    ** routine leaves an error message in pParse.zErrMsg where
    ** Build.FindTable() does not.
    */

        public static Table LocateTable(
            Parse pParse, /* context in which to report errors */
            int isView, /* True if looking for a VIEW rather than a TABLE */
            string zName, /* Name of the table we are looking for */
            string zDbase /* Name of the database.  Might be NULL */
            )
        {
            Table p;

            /* Read the database schema. If an error occurs, leave an error message
      ** and code in pParse and return NULL. */
            if (StatusCode.SQLITE_OK != Prepare.ReadSchema(pParse))
            {
                return null;
            }

            p = FindTable(pParse.db, zName, zDbase);
            if (p == null)
            {
                string zMsg = isView != 0 ? "no such view" : "no such table";
                if (zDbase != null)
                {
                    Utility.Sqlite3ErrorMsg(pParse, "%s: %s.%s", zMsg, zDbase, zName);
                }
                else
                {
                    Utility.Sqlite3ErrorMsg(pParse, "%s: %s", zMsg, zName);
                }
                pParse.checkSchema = 1;
            }
            return p;
        }

        /*
    ** Locate the in-memory structure that describes
    ** a particular index given the name of that index
    ** and the name of the database that contains the index.
    ** Return NULL if not found.
    **
    ** If zDatabase is 0, all databases are searched for the
    ** table and the first matching index is returned.  (No checking
    ** for duplicate index names is done.)  The search order is
    ** TEMP first, then MAIN, then any auxiliary databases added
    ** using the ATTACH command.
    */

        public static Index FindIndex(sqlite3 db, string zName, string zDb)
        {
            Index p = null;
            int i;
            int nName = Utility.Sqlite3Strlen30(zName);
            for (i = Const.OMIT_TEMPDB; i < db.nDb; i++)
            {
                int j = (i < 2) ? i ^ 1 : i; /* Search TEMP before MAIN */
                Schema pSchema = db.aDb[j].pSchema;
                Debug.Assert(pSchema != null);
                if (zDb != null && Utility.Sqlite3StrICmp(zDb, db.aDb[j].zName) != 0) continue;
                p = (Index) HashHelper.HashFind(pSchema.idxHash, zName, nName);
                if (p != null) break;
            }
            return p;
        }

        /*
    ** Reclaim the memory used by an index
    */

        private static void freeIndex(ref Index p)
        {
            sqlite3 db = p.pTable.dbMem;
#if !SQLITE_OMIT_ANALYZE
            sqlite3DeleteIndexSamples(p);
#endif
            MemPool.DbFree(db, ref p.zColAff);
            MemPool.DbFree(db, ref p);
        }

        /*
    ** Remove the given index from the index hash table, and free
    ** its memory structures.
    **
    ** The index is removed from the database hash tables but
    ** it is not unlinked from the Table that it indexes.
    ** Unlinking from the Table must be done by the calling function.
    */

        public static void DeleteIndex(Index p)
        {
            Index pOld;
            string zName = p.zName;

            pOld = (Index) HashHelper.HashInsert(ref p.pSchema.idxHash, zName,
                                             Utility.Sqlite3Strlen30(zName), null);
            Debug.Assert(pOld == null || pOld == p);
            freeIndex(ref p);
        }

        /*
    ** For the index called zIdxName which is found in the database iDb,
    ** unlike that index from its Table then remove the index from
    ** the index hash table and free all memory structures associated
    ** with the index.
    */

        public static void UnlinkAndDeleteIndex(sqlite3 db, int iDb, string zIdxName)
        {
            Index pIndex;
            int len;
            Hash pHash = db.aDb[iDb].pSchema.idxHash;

            len = Utility.Sqlite3Strlen30(zIdxName);
            pIndex = (Index) HashHelper.HashInsert(ref pHash, zIdxName, len, null);
            if (pIndex != null)
            {
                if (pIndex.pTable.pIndex == pIndex)
                {
                    pIndex.pTable.pIndex = pIndex.pNext;
                }
                else
                {
                    Index p;
                    /* Justification of UnitTest.ALWAYS();  The index must be on the list of
          ** indices. */
                    p = pIndex.pTable.pIndex;
                    while (UnitTest.ALWAYS(p != null) && p.pNext != pIndex)
                    {
                        p = p.pNext;
                    }
                    if (UnitTest.ALWAYS(p != null && p.pNext == pIndex))
                    {
                        p.pNext = pIndex.pNext;
                    }
                }
                freeIndex(ref pIndex);
            }
            db.flags |= Flag.SQLITE_InternChanges;
        }

        /*
    ** Erase all schema information from the in-memory hash tables of
    ** a single database.  This routine is called to reclaim memory
    ** before the database closes.  It is also called during a rollback
    ** if there were schema changes during the transaction or if a
    ** schema-cookie mismatch occurs.
    **
    ** If iDb==0 then reset the internal schema tables for all database
    ** files.  If iDb>=1 then reset the internal schema for only the
    ** single file indicated.
    */

        public static void ResetInternalSchema(sqlite3 db, int iDb)
        {
            int i, j;
            Debug.Assert(iDb >= 0 && iDb < db.nDb);

            if (iDb == 0)
            {
                sqlite3BtreeEnterAll(db);
            }
            for (i = iDb; i < db.nDb; i++)
            {
                Db pDb = db.aDb[i];
                if (pDb.pSchema != null)
                {
                    Debug.Assert(i == 1 || (pDb.pBt != null && sqlite3BtreeHoldsMutex(pDb.pBt)));
                    Debug.Assert(i == 1 || (pDb.pBt != null));
                    Callback.SchemaFree(pDb.pSchema);
                }
                if (iDb > 0) return;
            }
            Debug.Assert(iDb == 0);
            db.flags &= ~Flag.SQLITE_InternChanges;
            sqlite3VtabUnlockList(db);
            sqlite3BtreeLeaveAll(db);
            /* If one or more of the auxiliary database files has been closed,
      ** then remove them from the auxiliary database list.  We take the
      ** opportunity to do this here since we have just deleted all of the
      ** schema hash tables and therefore do not have to make any changes
      ** to any of those tables.
      */
            for (i = j = 2; i < db.nDb; i++)
            {
                Db pDb = db.aDb[i];
                if (pDb.pBt == null)
                {
                    MemPool.DbFree(db, ref pDb.zName);
                    continue;
                }
                if (j < i)
                {
                    db.aDb[j] = db.aDb[i];
                }
                j++;
            }
            if (db.nDb != j) db.aDb[j] = new Db(); //memset(db.aDb[j], 0, (db.nDb-j)*sizeof(db.aDb[j]));
            db.nDb = j;
            if (db.nDb <= 2 && db.aDb != db.aDbStatic)
            {
                Array.Copy(db.aDb, db.aDbStatic, 2); // memcpy(db.aDbStatic, db.aDb, 2*sizeof(db.aDb[0]));
                MemPool.DbFree(db, ref db.aDb);
                //db.aDb = db.aDbStatic;
            }
        }

        /*
    ** This routine is called when a commit occurs.
    */

        public static void CommitInternalChanges(sqlite3 db)
        {
            db.flags &= ~Flag.SQLITE_InternChanges;
        }

        /*
    ** Clear the column names from a table or view.
    */

        public static void ResetColumnNames(Table pTable)
        {
            int i;
            Column pCol;
            sqlite3 db = pTable.dbMem;
            UnitTest.TestCase(db == null);
            Debug.Assert(pTable != null);
            for (i = 0; i < pTable.nCol; i++)
            {
                pCol = pTable.aCol[i];
                if (pCol != null)
                {
                    MemPool.DbFree(db, ref pCol.zName);
                    ExprHelper.ExprDelete(db, ref pCol.pDflt);
                    MemPool.DbFree(db, ref pCol.zDflt);
                    MemPool.DbFree(db, ref pCol.zType);
                    MemPool.DbFree(db, ref pCol.zColl);
                }
            }
            pTable.aCol = null;
            MemPool.DbFree(db, ref pTable.aCol);
            pTable.nCol = 0;
        }


        /// <summary>
        ///  Remove the memory data structures associated with the given
        ///  Table.  No changes are made to disk by this routine.
        /// 
        ///  This routine just deletes the data structure.  It does not unlink
        ///  the table data structure from the hash table.  But it does destroy
        ///  memory structures of the indices and foreign keys associated with
        ///  the table.
        /// </summary>

        public static void DeleteTable(ref Table pTable)
        {
            Index pIndex;
            Index pNext;
            sqlite3 db;

            if (pTable == null) return;
            db = pTable.dbMem;
            UnitTest.TestCase(db == null);

            /* Do not delete the table until the reference count reaches zero. */
            pTable.nRef--;
            if (pTable.nRef > 0)
            {
                return;
            }
            Debug.Assert(pTable.nRef == 0);

            /* Delete all indices associated with this table
      */
            for (pIndex = pTable.pIndex; pIndex != null; pIndex = pNext)
            {
                pNext = pIndex.pNext;
                Debug.Assert(pIndex.pSchema == pTable.pSchema);
                DeleteIndex(pIndex);
            }

            /* Delete any foreign keys attached to this table. */
            sqlite3FkDelete(pTable);

            /* Delete the Table structure itself.
*/
            ResetColumnNames(pTable);
            MemPool.DbFree(db, ref pTable.zName);
            MemPool.DbFree(db, ref pTable.zColAff);
            sqlite3SelectDelete(db, ref pTable.pSelect);
#if !SQLITE_OMIT_CHECK
            ExprHelper.ExprDelete(db, ref pTable.pCheck);
#endif
            sqlite3VtabClear(pTable);
            MemPool.DbFree(db, ref pTable);
        }

        /*
    ** Unlink the given table from the hash tables and the delete the
    ** table structure with all its indices and foreign keys.
    */

        public static void UnlinkAndDeleteTable(sqlite3 db, int iDb, string zTabName)
        {
            Table p;
            Db pDb;

            Debug.Assert(db != null);
            Debug.Assert(iDb >= 0 && iDb < db.nDb);
            Debug.Assert(zTabName != null);
            UnitTest.TestCase(zTabName.Length == 0); /* Zero-length table names are allowed */
            pDb = db.aDb[iDb];
            p = (Table) HashHelper.HashInsert(ref pDb.pSchema.tblHash, zTabName,
                                          Utility.Sqlite3Strlen30(zTabName), null);
            DeleteTable(ref p);
            db.flags |= Flag.SQLITE_InternChanges;
        }

        /*
    ** Given a token, return a string that consists of the text of that
    ** token.  Space to hold the returned string
    ** is obtained from sqliteMalloc() and must be freed by the calling
    ** function.
    **
    ** Any quotation marks (ex:  "name", 'name', [name], or `name`) that
    ** surround the body of the token are removed.
    **
    ** Tokens are often just pointers into the original SQL text and so
    ** are not \000 terminated and are not persistent.  The returned string
    ** is \000 terminated and is persistent.
    */

        public static string NameFromToken(sqlite3 db, Token pName)
        {
            string zName;
            if (pName != null && pName.z != null)
            {
                zName = pName.z.Substring(0, pName.n); //sqlite3DbStrNDup(db, (char*)pName.z, pName.n);
                Utility.Sqlite3Dequote(ref zName);
            }
            else
            {
                return null;
            }
            return zName;
        }

        /*
    ** Open the sqlite_master table stored in database number iDb for
    ** writing. The table is opened using cursor 0.
    */

        public static void OpenMasterTable(Parse p, int iDb)
        {
            Vdbe v = SelectHelper.GetVdbe(p);
            sqlite3TableLock(p, iDb, Const.MASTER_ROOT, 1, Helper.SchemaTable(iDb));
            VdbeAux.VdbeAddOp3(v, OPCode.OP_OpenWrite, 0, Const.MASTER_ROOT, iDb);
            VdbeAux.VdbeChangeP4(v, -1, 5, P4Type.P4_INT32); /* 5 column table */
            if (p.nTab == 0)
            {
                p.nTab = 1;
            }
        }

        /*
    ** Parameter zName points to a nul-terminated buffer containing the name
    ** of a database ("main", "temp" or the name of an attached db). This
    ** function returns the index of the named database in db->aDb[], or
    ** -1 if the named db cannot be found.
    */

        public static int FindDbName(sqlite3 db, string zName)
        {
            int i = -1; /* Database number */
            if (zName != null)
            {
                Db pDb;
                int n = Utility.Sqlite3Strlen30(zName);
                for (i = (db.nDb - 1); i >= 0; i--)
                {
                    pDb = db.aDb[i];
                    if ((Const.OMIT_TEMPDB == 0 || i != 1) && n == Utility.Sqlite3Strlen30(pDb.zName) &&
                        0 == Utility.Sqlite3StrICmp(pDb.zName, zName))
                    {
                        break;
                    }
                }
            }
            return i;
        }

        /*
    ** The token *pName contains the name of a database (either "main" or
    ** "temp" or the name of an attached db). This routine returns the
    ** index of the named database in db->aDb[], or -1 if the named db
    ** does not exist.
    */

        public static int FindDb(sqlite3 db, Token pName)
        {
            int i; /* Database number */
            string zName; /* Name we are searching for */
            zName = NameFromToken(db, pName);
            i = FindDbName(db, zName);
            MemPool.DbFree(db, ref zName);
            return i;
        }

        /* The table or view or trigger name is passed to this routine via tokens
    ** pName1 and pName2. If the table name was fully qualified, for example:
    **
    ** CREATE TABLE xxx.yyy (...);
    **
    ** Then pName1 is set to "xxx" and pName2 "yyy". On the other hand if
    ** the table name is not fully qualified, i.e.:
    **
    ** CREATE TABLE yyy(...);
    **
    ** Then pName1 is set to "yyy" and pName2 is "".
    **
    ** This routine sets the ppUnqual pointer to point at the token (pName1 or
    ** pName2) that stores the unqualified table name.  The index of the
    ** database "xxx" is returned.
    */

        public static int TwoPartName(
            Parse pParse, /* Parsing and code generating context */
            Token pName1, /* The "xxx" in the name "xxx.yyy" or "xxx" */
            Token pName2, /* The "yyy" in the name "xxx.yyy" */
            ref Token pUnqual /* Write the unqualified object name here */
            )
        {
            int iDb; /* Database holding the object */
            sqlite3 db = pParse.db;

            if (UnitTest.ALWAYS(pName2 != null) && pName2.n > 0)
            {
                if (db.init.busy != 0)
                {
                    Utility.Sqlite3ErrorMsg(pParse, "corrupt database");
                    pParse.nErr++;
                    return -1;
                }
                pUnqual = pName2;
                iDb = FindDb(db, pName1);
                if (iDb < 0)
                {
                    Utility.Sqlite3ErrorMsg(pParse, "unknown database %T", pName1);
                    pParse.nErr++;
                    return -1;
                }
            }
            else
            {
                Debug.Assert(db.init.iDb == 0 || db.init.busy != 0);
                iDb = db.init.iDb;
                pUnqual = pName1;
            }
            return iDb;
        }

        /*
    ** This routine is used to check if the UTF-8 string zName is a legal
    ** unqualified name for a new schema object (table, index, view or
    ** trigger). All names are legal except those that begin with the string
    ** "sqlite_" (in upper, lower or mixed case). This portion of the namespace
    ** is reserved for internal use.
    */

        public static int CheckObjectName(Parse pParse, string zName)
        {
            if (0 == pParse.db.init.busy && pParse.nested == 0
                && (pParse.db.flags & Flag.SQLITE_WriteSchema) == 0
                && 0 == Utility.Sqlite3StrNICmp(zName, "sqlite_", 7))
            {
                Utility.Sqlite3ErrorMsg(pParse, "object name reserved for internal use: %s", zName);
                return StatusCode.SQLITE_ERROR;
            }
            return StatusCode.SQLITE_OK;
        }

        /*
    ** Begin constructing a new table representation in memory.  This is
    ** the first of several action routines that get called in response
    ** to a CREATE TABLE statement.  In particular, this routine is called
    ** after seeing tokens "CREATE" and "TABLE" and the table name. The isTemp
    ** flag is true if the table should be stored in the auxiliary database
    ** file instead of in the main database file.  This is normally the case
    ** when the "TEMP" or "TEMPORARY" keyword occurs in between
    ** CREATE and TABLE.
    **
    ** The new table record is initialized and put in pParse.pNewTable.
    ** As more of the CREATE TABLE statement is parsed, additional action
    ** routines will be called to add more information to this record.
    ** At the end of the CREATE TABLE statement, the Build.EndTable() routine
    ** is called to complete the construction of the new table record.
    */

        public static void StartTable(
            Parse pParse, /* Parser context */
            Token pName1, /* First part of the name of the table or view */
            Token pName2, /* Second part of the name of the table or view */
            int isTemp, /* True if this is a TEMP table */
            int isView, /* True if this is a VIEW */
            int isVirtual, /* True if this is a VIRTUAL table */
            int noErr /* Do nothing if table already exists */
            )
        {
            Table pTable;
            string zName = null; /* The name of the new table */
            sqlite3 db = pParse.db;
            Vdbe v;
            int iDb; /* Database number to create the table in */
            var pName = new Token(); /* Unqualified name of the table to create */

            /* The table or view name to create is passed to this routine via tokens
      ** pName1 and pName2. If the table name was fully qualified, for example:
      **
      ** CREATE TABLE xxx.yyy (...);
      **
      ** Then pName1 is set to "xxx" and pName2 "yyy". On the other hand if
      ** the table name is not fully qualified, i.e.:
      **
      ** CREATE TABLE yyy(...);
      **
      ** Then pName1 is set to "yyy" and pName2 is "".
      **
      ** The call below sets the pName pointer to point at the token (pName1 or
      ** pName2) that stores the unqualified table name. The variable iDb is
      ** set to the index of the database that the table or view is to be
      ** created in.
      */
            iDb = TwoPartName(pParse, pName1, pName2, ref pName);
            if (iDb < 0) return;
            if (Const.OMIT_TEMPDB == 0 && isTemp != 0 && iDb > 1)
            {
                /* If creating a temp table, the name may not be qualified */
                Utility.Sqlite3ErrorMsg(pParse, "temporary table name must be unqualified");
                return;
            }
            if (Const.OMIT_TEMPDB == 0 && isTemp != 0) iDb = 1;

            pParse.sNameToken = pName;
            zName = NameFromToken(db, pName);
            if (zName == null) return;
            if (StatusCode.SQLITE_OK != CheckObjectName(pParse, zName))
            {
                goto begin_table_error;
            }
            if (db.init.iDb == 1) isTemp = 1;
#if !SQLITE_OMIT_AUTHORIZATION
Debug.Assert( (isTemp & 1)==isTemp );
{
int code;
char *zDb = db.aDb[iDb].zName;
if( sqlite3AuthCheck(pParse, ActionCode.SQLITE_INSERT, Helper.SchemaTable(isTemp), 0, zDb) ){
goto begin_table_error;
}
if( isView ){
if( Const.OMIT_TEMPDB ==0&& isTemp ){
code = ActionCode.SQLITE_CREATE_TEMP_VIEW;
}else{
code = ActionCode.SQLITE_CREATE_VIEW;
}
}else{
if( Const.OMIT_TEMPDB ==0&& isTemp ){
code = ActionCode.SQLITE_CREATE_TEMP_TABLE;
}else{
code = ActionCode.SQLITE_CREATE_TABLE;
}
}
if( !isVirtual && sqlite3AuthCheck(pParse, code, zName, 0, zDb) ){
goto begin_table_error;
}
}
#endif

            /* Make sure the new table name does not collide with an existing
** index or table name in the same database.  Issue an error message if
** it does. The exception is if the statement being parsed was passed
** to an sqlite3_declare_vtab() call. In that case only the column names
** and types will be used, so there is no need to test for namespace
** collisions.
*/
            if (!Const.IN_DECLARE_VTAB)
            {
                if (StatusCode.SQLITE_OK != Prepare.ReadSchema(pParse))
                {
                    goto begin_table_error;
                }
                pTable = FindTable(db, zName, db.aDb[iDb].zName);
                if (pTable != null)
                {
                    if (noErr == 0)
                    {
                        Utility.Sqlite3ErrorMsg(pParse, "table %T already exists", pName);
                    }
                    goto begin_table_error;
                }
                if (FindIndex(db, zName, null) != null && (iDb == 0 || 0 == db.init.busy))
                {
                    Utility.Sqlite3ErrorMsg(pParse, "there is already an index named %s", zName);
                    goto begin_table_error;
                }
            }

            pTable = new Table(); // Malloc.DbMallocZero(db, Table).Length;
            if (pTable == null)
            {
                //        db.mallocFailed = 1;
                pParse.rc = StatusCode.SQLITE_NOMEM;
                pParse.nErr++;
                goto begin_table_error;
            }
            pTable.zName = zName;
            pTable.iPKey = -1;
            pTable.pSchema = db.aDb[iDb].pSchema;
            pTable.nRef = 1;
            pTable.dbMem = null;
            Debug.Assert(pParse.pNewTable == null);
            pParse.pNewTable = pTable;

            /* If this is the magic sqlite_sequence table used by autoincrement,
      ** then record a pointer to this table in the main database structure
      ** so that INSERT can find the table easily.
      */
#if !SQLITE_OMIT_AUTOINCREMENT
            if (pParse.nested == 0 && zName == "sqlite_sequence")
            {
                pTable.pSchema.pSeqTab = pTable;
            }
#endif

            /* Begin generating the code that will insert the table record into
** the SQLITE_MASTER table.  Note in particular that we must go ahead
** and allocate the record number for the table entry now.  Before any
** PRIMARY KEY or UNIQUE keywords are parsed.  Those keywords will cause
** indices to be created and the table record must come before the
** indices.  Hence, the record number for the table must be allocated
** now.
*/
            if (0 == db.init.busy && (v = SelectHelper.GetVdbe(pParse)) != null)
            {
                int j1;
                int fileFormat;
                int reg1, reg2, reg3;
                BeginWriteOperation(pParse, 0, iDb);

                if (isVirtual != 0)
                {
                    VdbeAux.VdbeAddOp0(v, OPCode.OP_VBegin);
                }

                /* If the file format and encoding in the database have not been set,
        ** set them now.
        */
                reg1 = pParse.regRowid = ++pParse.nMem;
                reg2 = pParse.regRoot = ++pParse.nMem;
                reg3 = ++pParse.nMem;
                VdbeAux.VdbeAddOp3(v, OPCode.OP_ReadCookie, iDb, reg3, BtreeMeta.BTREE_FILE_FORMAT);
                VdbeAux.VdbeUsesBtree(v, iDb);
                j1 = VdbeAux.VdbeAddOp1(v, OPCode.OP_If, reg3);
                fileFormat = (db.flags & Flag.SQLITE_LegacyFileFmt) != 0
                                 ? 1
                                 : Const.SQLITE_MAX_FILE_FORMAT;
                VdbeAux.VdbeAddOp2(v, OPCode.OP_Integer, fileFormat, reg3);
                VdbeAux.VdbeAddOp3(v, OPCode.OP_SetCookie, iDb, BtreeMeta.BTREE_FILE_FORMAT, reg3);
                VdbeAux.VdbeAddOp2(v, OPCode.OP_Integer, Helper.ENC(db), reg3);
                VdbeAux.VdbeAddOp3(v, OPCode.OP_SetCookie, iDb, BtreeMeta.BTREE_TEXT_ENCODING, reg3);
                VdbeAux.VdbeJumpHere(v, j1);

                /* This just creates a place-holder record in the sqlite_master table.
        ** The record created does not contain anything yet.  It will be replaced
        ** by the real entry in code generated at Build.EndTable().
        **
        ** The rowid for the new entry is left in register pParse->regRowid.
        ** The root page number of the new table is left in reg pParse->regRoot.
        ** The rowid and root page number values are needed by the code that
        ** Build.EndTable will generate.
        */
                if (isView != 0 || isVirtual != 0)
                {
                    VdbeAux.VdbeAddOp2(v, OPCode.OP_Integer, 0, reg2);
                }
                else
                {
                    VdbeAux.VdbeAddOp2(v, OPCode.OP_CreateTable, iDb, reg2);
                }
                Build.OpenMasterTable(pParse, iDb);
                VdbeAux.VdbeAddOp2(v, OPCode.OP_NewRowid, 0, reg1);
                VdbeAux.VdbeAddOp2(v, OPCode.OP_Null, 0, reg3);
                VdbeAux.VdbeAddOp3(v, OPCode.OP_Insert, 0, reg3, reg1);
                VdbeAux.VdbeChangeP5(v, P5Value.OPFLAG_APPEND);
                VdbeAux.VdbeAddOp0(v, OPCode.OP_Close);
            }

            /* Normal (non-error) return. */
            return;

            /* If an error occurs, we jump here */
            begin_table_error:
            MemPool.DbFree(db, ref zName);
            return;
        }

        /*
    ** This macro is used to compare two strings in a case-insensitive manner.
    ** It is slightly faster than calling Utility.Sqlite3StrICmp() directly, but
    ** produces larger code.
    **
    ** WARNING: This macro is not compatible with the strcmp() family. It
    ** returns true if the two strings are equal, otherwise false.
    */
        //#define STRICMP(x, y) (\
        //Global.Sqlite3UpperToLower[*(unsigned char *)(x)]==   \
        //Global.Sqlite3UpperToLower[*(unsigned char *)(y)]     \
        //&& Utility.Sqlite3StrICmp((x)+1,(y)+1)==0 )

        /*
    ** Add a new column to the table currently being constructed.
    **
    ** The parser calls this routine once for each column declaration
    ** in a CREATE TABLE statement.  Build.StartTable() gets called
    ** first to get things going.  Then this routine is called for each
    ** column.
    */

        public static void AddColumn(Parse pParse, Token pName)
        {
            Table p;
            int i;
            string z;
            Column pCol;
            sqlite3 db = pParse.db;
            if ((p = pParse.pNewTable) == null) return;
#if SQLITE_MAX_COLUMN || !SQLITE_MAX_COLUMN
            if (p.nCol + 1 > db.aLimit[LimitCategory.SQLITE_LIMIT_COLUMN])
            {
                Utility.Sqlite3ErrorMsg(pParse, "too many columns on %s", p.zName);
                return;
            }
#endif
            z = NameFromToken(db, pName);
            if (z == null) return;
            for (i = 0; i < p.nCol; i++)
            {
                if (0 == Utility.Sqlite3StrICmp(z, p.aCol[i].zName))
                {
//STRICMP(z, p.aCol[i].zName) ){
                    Utility.Sqlite3ErrorMsg(pParse, "duplicate column name: %s", z);
                    MemPool.DbFree(db, ref z);
                    return;
                }
            }
            if ((p.nCol & 0x7) == 0)
            {
                //aNew = Malloc.DbRealloc(db,p.aCol,(p.nCol+8)*sizeof(p.aCol[0]));
                //if( aNew==0 ){
                //  MemPool.DbFree(db,ref z);
                //  return;
                //}
                Array.Resize(ref p.aCol, p.nCol + 8);
            }
            p.aCol[p.nCol] = new Column();
            pCol = p.aCol[p.nCol];
            //memset(pCol, 0, sizeof(p.aCol[0]));
            pCol.zName = z;

            /* If there is no type specified, columns have the default affinity
      ** 'NONE'. If there is a type specified, then Build.AddColumnType() will
      ** be called next to set pCol.affinity correctly.
      */
            pCol.affinity = ColumnAffinityType.SQLITE_AFF_NONE;
            p.nCol++;
        }

        /*
    ** This routine is called by the parser while in the middle of
    ** parsing a CREATE TABLE statement.  A "NOT NULL" constraint has
    ** been seen on a column.  This routine sets the notNull flag on
    ** the column currently under construction.
    */

        public static void AddNotNull(Parse pParse, int onError)
        {
            Table p;
            p = pParse.pNewTable;
            if (p == null || UnitTest.NEVER(p.nCol < 1)) return;
            p.aCol[p.nCol - 1].notNull = (byte) onError;
        }

        /*
    ** Scan the column type name zType (length nType) and return the
    ** associated affinity type.
    **
    ** This routine does a case-independent search of zType for the
    ** substrings in the following table. If one of the substrings is
    ** found, the corresponding affinity is returned. If zType contains
    ** more than one of the substrings, entries toward the top of
    ** the table take priority. For example, if zType is 'BLOBINT',
    ** ColumnAffinityType.SQLITE_AFF_INTEGER is returned.
    **
    ** Substring     | Affinity
    ** --------------------------------
    ** 'INT'         | ColumnAffinityType.SQLITE_AFF_INTEGER
    ** 'CHAR'        | ColumnAffinityType.SQLITE_AFF_TEXT
    ** 'CLOB'        | ColumnAffinityType.SQLITE_AFF_TEXT
    ** 'TEXT'        | ColumnAffinityType.SQLITE_AFF_TEXT
    ** 'BLOB'        | ColumnAffinityType.SQLITE_AFF_NONE
    ** 'REAL'        | ColumnAffinityType.SQLITE_AFF_REAL
    ** 'FLOA'        | ColumnAffinityType.SQLITE_AFF_REAL
    ** 'DOUB'        | ColumnAffinityType.SQLITE_AFF_REAL
    **
    ** If none of the substrings in the above table are found,
    ** ColumnAffinityType.SQLITE_AFF_NUMERIC is returned.
    */

        public static char AffinityType(string zIn)
        {
            //uint h = 0;
            //char aff = ColumnAffinityType.SQLITE_AFF_NUMERIC;
            zIn = zIn.ToLower();
            if (zIn.Contains("char") || zIn.Contains("clob") || zIn.Contains("text")) return ColumnAffinityType.SQLITE_AFF_TEXT;
            if (zIn.Contains("blob")) return ColumnAffinityType.SQLITE_AFF_NONE;
            if (zIn.Contains("doub") || zIn.Contains("floa") || zIn.Contains("real")) return ColumnAffinityType.SQLITE_AFF_REAL;
            if (zIn.Contains("int")) return ColumnAffinityType.SQLITE_AFF_INTEGER;
            return ColumnAffinityType.SQLITE_AFF_NUMERIC;
            //      string zEnd = pType.z.Substring(pType.n);

            //      while( zIn!=zEnd ){
            //        h = (h<<8) + Global.Sqlite3UpperToLower[*zIn];
            //        zIn++;
            //        if( h==(('c'<<24)+('h'<<16)+('a'<<8)+'r') ){             /* CHAR */
            //          aff = ColumnAffinityType.SQLITE_AFF_TEXT;
            //        }else if( h==(('c'<<24)+('l'<<16)+('o'<<8)+'b') ){       /* CLOB */
            //          aff = ColumnAffinityType.SQLITE_AFF_TEXT;
            //        }else if( h==(('t'<<24)+('e'<<16)+('x'<<8)+'t') ){       /* TEXT */
            //          aff = ColumnAffinityType.SQLITE_AFF_TEXT;
            //        }else if( h==(('b'<<24)+('l'<<16)+('o'<<8)+'b')          /* BLOB */
            //            && (aff==ColumnAffinityType.SQLITE_AFF_NUMERIC || aff==ColumnAffinityType.SQLITE_AFF_REAL) ){
            //          aff = ColumnAffinityType.SQLITE_AFF_NONE;
            //#if !SQLITE_OMIT_FLOATING_POINT
            //        }else if( h==(('r'<<24)+('e'<<16)+('a'<<8)+'l')          /* REAL */
            //            && aff==ColumnAffinityType.SQLITE_AFF_NUMERIC ){
            //          aff = ColumnAffinityType.SQLITE_AFF_REAL;
            //        }else if( h==(('f'<<24)+('l'<<16)+('o'<<8)+'a')          /* FLOA */
            //            && aff==ColumnAffinityType.SQLITE_AFF_NUMERIC ){
            //          aff = ColumnAffinityType.SQLITE_AFF_REAL;
            //        }else if( h==(('d'<<24)+('o'<<16)+('u'<<8)+'b')          /* DOUB */
            //            && aff==ColumnAffinityType.SQLITE_AFF_NUMERIC ){
            //          aff = ColumnAffinityType.SQLITE_AFF_REAL;
            //#endif
            //        }else if( (h&0x00FFFFFF)==(('i'<<16)+('n'<<8)+'t') ){    /* INT */
            //          aff = ColumnAffinityType.SQLITE_AFF_INTEGER;
            //          break;
            //        }
            //      }

            //      return aff;
        }

        /*
    ** This routine is called by the parser while in the middle of
    ** parsing a CREATE TABLE statement.  The pFirst token is the first
    ** token in the sequence of tokens that describe the type of the
    ** column currently under construction.   pLast is the last token
    ** in the sequence.  Use this information to construct a string
    ** that contains the typename of the column and store that string
    ** in zType.
    */

        public static void AddColumnType(Parse pParse, Token pType)
        {
            Table p;
            Column pCol;

            p = pParse.pNewTable;
            if (p == null || UnitTest.NEVER(p.nCol < 1)) return;
            pCol = p.aCol[p.nCol - 1];
            Debug.Assert(pCol.zType == null);
            pCol.zType = NameFromToken(pParse.db, pType);
            pCol.affinity = AffinityType(pCol.zType);
        }


        /*
    ** The expression is the default value for the most recently added column
    ** of the table currently under construction.
    **
    ** Default value expressions must be constant.  Raise an exception if this
    ** is not the case.
    **
    ** This routine is called by the parser while in the middle of
    ** parsing a CREATE TABLE statement.
    */

        public static void AddDefaultValue(Parse pParse, ExprSpan pSpan)
        {
            Table p;
            Column pCol;
            sqlite3 db = pParse.db;
            p = pParse.pNewTable;
            if (p != null)
            {
                pCol = (p.aCol[p.nCol - 1]);
                if (ExprHelper.ExprIsConstantOrFunction(pSpan.pExpr) == 0)
                {
                    Utility.Sqlite3ErrorMsg(pParse, "default value of column [%s] is not constant",
                                    pCol.zName);
                }
                else
                {
                    /* A copy of pExpr is used instead of the original, as pExpr contains
          ** tokens that point to volatile memory. The 'span' of the expression
          ** is required by pragma table_info.
          */
                    ExprHelper.ExprDelete(db, ref pCol.pDflt);
                    pCol.pDflt = ExprHelper.ExprDup(db, pSpan.pExpr, Const.EXPRDUP_REDUCE);
                    MemPool.DbFree(db, ref pCol.zDflt);
                    pCol.zDflt = pSpan.zStart.Substring(0, pSpan.zStart.Length - pSpan.zEnd.Length);
                    //sqlite3DbStrNDup( db, pSpan.zStart,
                    //                               (int)( pSpan.zEnd.Length - pSpan.zStart.Length ) );
                }
            }
            ExprHelper.ExprDelete(db, ref pSpan.pExpr);
        }

        /*
    ** Designate the PRIMARY KEY for the table.  pList is a list of names
    ** of columns that form the primary key.  If pList is NULL, then the
    ** most recently added column of the table is the primary key.
    **
    ** A table can have at most one primary key.  If the table already has
    ** a primary key (and this is the second primary key) then create an
    ** error.
    **
    ** If the PRIMARY KEY is on a single column whose datatype is INTEGER,
    ** then we will try to use that column as the rowid.  Set the Table.iPKey
    ** field of the table under construction to be the index of the
    ** INTEGER PRIMARY KEY column.  Table.iPKey is set to -1 if there is
    ** no INTEGER PRIMARY KEY.
    **
    ** If the key is not an INTEGER PRIMARY KEY, then create a unique
    ** index for the key.  No index is created for INTEGER PRIMARY KEYs.
    */
        // OVERLOADS, so I don't need to rewrite parse.c
        public static void AddPrimaryKey(Parse pParse, int null_2, int onError, int autoInc, int sortOrder)
        {
            AddPrimaryKey(pParse, null, onError, autoInc, sortOrder);
        }

        public static void AddPrimaryKey(
            Parse pParse, /* Parsing context */
            ExprList pList, /* List of field names to be indexed */
            int onError, /* What to do with a uniqueness conflict */
            int autoInc, /* True if the AUTOINCREMENT keyword is present */
            int sortOrder /* Const.SQLITE_SO_ASC or Const.SQLITE_SO_DESC */
            )
        {
            Table pTab = pParse.pNewTable;
            string zType = null;
            int iCol = -1, i;
            if (pTab == null || Const.IN_DECLARE_VTAB) goto primary_key_exit;
            if ((pTab.tabFlags & TabFlag.TF_HasPrimaryKey) != 0)
            {
                Utility.Sqlite3ErrorMsg(pParse,
                                "table \"%s\" has more than one primary key", pTab.zName);
                goto primary_key_exit;
            }
            pTab.tabFlags |= TabFlag.TF_HasPrimaryKey;
            if (pList == null)
            {
                iCol = pTab.nCol - 1;
                pTab.aCol[iCol].isPrimKey = 1;
            }
            else
            {
                for (i = 0; i < pList.nExpr; i++)
                {
                    for (iCol = 0; iCol < pTab.nCol; iCol++)
                    {
                        if (Utility.Sqlite3StrICmp(pList.a[i].zName, pTab.aCol[iCol].zName) == 0)
                        {
                            break;
                        }
                    }
                    if (iCol < pTab.nCol)
                    {
                        pTab.aCol[iCol].isPrimKey = 1;
                    }
                }
                if (pList.nExpr > 1) iCol = -1;
            }
            if (iCol >= 0 && iCol < pTab.nCol)
            {
                zType = pTab.aCol[iCol].zType;
            }
            if (zType != null && Utility.Sqlite3StrICmp(zType, "INTEGER") == 0
                && sortOrder == Const.SQLITE_SO_ASC)
            {
                pTab.iPKey = iCol;
                pTab.keyConf = (byte) onError;
                Debug.Assert(autoInc == 0 || autoInc == 1);
                pTab.tabFlags |= (byte) (autoInc*TabFlag.TF_Autoincrement);
            }
            else if (autoInc != 0)
            {
#if !SQLITE_OMIT_AUTOINCREMENT
                Utility.Sqlite3ErrorMsg(pParse, "AUTOINCREMENT is only allowed on an " +
                                        "INTEGER PRIMARY KEY");
#endif
            }
            else
            {
                Index p;
                p = CreateIndex(pParse, 0, 0, 0, pList, onError, 0, 0, sortOrder, 0);
                if (p != null)
                {
                    p.autoIndex = 2;
                }
                pList = null;
            }

            primary_key_exit:
            ExprHelper.ExprListDelete(pParse.db, ref pList);
            return;
        }

        /*
    ** Add a new CHECK constraint to the table currently under construction.
    */

        public static void AddCheckConstraint(
            Parse pParse, /* Parsing context */
            Expr pCheckExpr /* The check expression */
            )
        {
            sqlite3 db = pParse.db;
#if !SQLITE_OMIT_CHECK
            Table pTab = pParse.pNewTable;
            if (pTab != null && !Const.IN_DECLARE_VTAB)
            {
                pTab.pCheck = ExprHelper.ExprAnd(db, pTab.pCheck, pCheckExpr);
            }
            else
#endif
            {
                ExprHelper.ExprDelete(db, ref pCheckExpr);
            }
        }

        /*
    ** Set the collation function of the most recently parsed table column
    ** to the CollSeq given.
    */

        public static void AddCollateType(Parse pParse, Token pToken)
        {
            Table p;
            int i;
            string zColl; /* Dequoted name of collation sequence */
            sqlite3 db;

            if ((p = pParse.pNewTable) == null) return;
            i = p.nCol - 1;
            db = p.dbMem;
            zColl = NameFromToken(db, pToken);
            if (zColl == null) return;

            if (Build.LocateCollSeq(pParse, zColl) != null)
            {
                Index pIdx;
                p.aCol[i].zColl = zColl;

                /* If the column is declared as "<name> PRIMARY KEY COLLATE <type>",
        ** then an index may have been created on this column before the
        ** collation type was added. Correct this if it is the case.
        */
                for (pIdx = p.pIndex; pIdx != null; pIdx = pIdx.pNext)
                {
                    Debug.Assert(pIdx.nColumn == 1);
                    if (pIdx.aiColumn[0] == i)
                    {
                        pIdx.azColl[0] = p.aCol[i].zColl;
                    }
                }
            }
            else
            {
                MemPool.DbFree(db, ref zColl);
            }
        }

        /*
    ** This function returns the collation sequence for database native text
    ** encoding identified by the string zName, length nName.
    **
    ** If the requested collation sequence is not available, or not available
    ** in the database native encoding, the collation factory is invoked to
    ** request it. If the collation factory does not supply such a sequence,
    ** and the sequence is available in another text encoding, then that is
    ** returned instead.
    **
    ** If no versions of the requested collations sequence are available, or
    ** another error occurs, NULL is returned and an error message written into
    ** pParse.
    **
    ** This routine is a wrapper around Callback.FindCollSeq().  This routine
    ** invokes the collation factory if the named collation cannot be found
    ** and generates an error message.
    **
    ** See also: Callback.FindCollSeq(), Callback.GetCollSeq()
    */

        public static CollSeq LocateCollSeq(Parse pParse, string zName)
        {
            sqlite3 db = pParse.db;
            byte enc = db.aDb[0].pSchema.enc; // Helper.ENC(db);
            byte initbusy = db.init.busy;
            CollSeq pColl;

            pColl = Callback.FindCollSeq(db, enc, zName, initbusy);
            if (0 == initbusy && (pColl == null || pColl.xCmp == null))
            {
                pColl = Callback.GetCollSeq(db, enc, pColl, zName);
                if (pColl == null)
                {
                    Utility.Sqlite3ErrorMsg(pParse, "no such collation sequence: %s", zName);
                }
            }

            return pColl;
        }


        /*
    ** Generate code that will increment the schema cookie.
    **
    ** The schema cookie is used to determine when the schema for the
    ** database changes.  After each schema change, the cookie value
    ** changes.  When a process first reads the schema it records the
    ** cookie.  Thereafter, whenever it goes to access the database,
    ** it checks the cookie to make sure the schema has not changed
    ** since it was last read.
    **
    ** This plan is not completely bullet-proof.  It is possible for
    ** the schema to change multiple times and for the cookie to be
    ** set back to prior value.  But schema changes are infrequent
    ** and the probability of hitting the same cookie value is only
    ** 1 chance in 2^32.  So we're safe enough.
    */

        public static void ChangeCookie(Parse pParse, int iDb)
        {
            int r1 = ExprHelper.GetTempReg(pParse);
            sqlite3 db = pParse.db;
            Vdbe v = pParse.pVdbe;
            VdbeAux.VdbeAddOp2(v, OPCode.OP_Integer, db.aDb[iDb].pSchema.schema_cookie + 1, r1);
            VdbeAux.VdbeAddOp3(v, OPCode.OP_SetCookie, iDb, BtreeMeta.BTREE_SCHEMA_VERSION, r1);
            ExprHelper.ReleaseTempReg(pParse, r1);
        }

        /*
    ** Measure the number of characters needed to output the given
    ** identifier.  The number returned includes any quotes used
    ** but does not include the null terminator.
    **
    ** The estimate is conservative.  It might be larger that what is
    ** really needed.
    */

        private static int identLength(string z)
        {
            int n;
            for (n = 0; n < z.Length; n++)
            {
                if (z[n] == (byte) '"')
                {
                    n++;
                }
            }
            return n + 2;
        }


        /*
    ** The first parameter is a pointer to an output buffer. The second
    ** parameter is a pointer to an integer that contains the offset at
    ** which to write into the output buffer. This function copies the
    ** nul-terminated string pointed to by the third parameter, zSignedIdent,
    ** to the specified offset in the buffer and updates *pIdx to refer
    ** to the first byte after the last byte written before returning.
    **
    ** If the string zSignedIdent consists entirely of alpha-numeric
    ** characters, does not begin with a digit and is not an SQL keyword,
    ** then it is copied to the output buffer exactly as it is. Otherwise,
    ** it is quoted using double-quotes.
    */

        private static void identPut(StringBuilder z, ref int pIdx, string zSignedIdent)
        {
            string zIdent = zSignedIdent;
            int i;
            int j;
            bool needQuote;
            i = pIdx;
            for (j = 0; j < zIdent.Length; j++)
            {
                if (!SqliteInt.Sqlite3Isalnum(zIdent[j]) && zIdent[j] != '_') break;
            }
            needQuote = SqliteInt.Sqlite3Isdigit(zIdent[0]) || KeywordHash.KeywordCode(zIdent, j) != TokenKeyword.TK_ID;
            if (!needQuote)
            {
                needQuote = (j < zIdent.Length && zIdent[j] != 0);
            }
            if (needQuote)
            {
                if (i == z.Length) z.Append('\0');
                z[i++] = '"';
            }
            for (j = 0; j < zIdent.Length; j++)
            {
                if (i == z.Length) z.Append('\0');
                z[i++] = zIdent[j];
                if (zIdent[j] == '"')
                {
                    if (i == z.Length) z.Append('\0');
                    z[i++] = '"';
                }
            }
            if (needQuote)
            {
                if (i == z.Length) z.Append('\0');
                z[i++] = '"';
            }
            //z[i] = 0;
            pIdx = i;
        }

        /*
    ** Generate a CREATE TABLE statement appropriate for the given
    ** table.  Memory to hold the text of the statement is obtained
    ** from sqliteMalloc() and must be freed by the calling function.
    */

        private static string createTableStmt(sqlite3 db, Table p)
        {
            int i, k, n;
            StringBuilder zStmt;
            string zSep;
            string zSep2;
            string zEnd;
            Column pCol;
            n = 0;
            for (i = 0; i < p.nCol; i++)
            {
//, pCol++){
                pCol = p.aCol[i];
                n += identLength(pCol.zName) + 5;
            }
            n += identLength(p.zName);
            if (n < 50)
            {
                zSep = "";
                zSep2 = ",";
                zEnd = ")";
            }
            else
            {
                zSep = "\n  ";
                zSep2 = ",\n  ";
                zEnd = "\n)";
            }
            n += 35 + 6*p.nCol;
            zStmt = new StringBuilder(n);
            //zStmt = Malloc.sqlite3Malloc( n );
            //if( zStmt==0 ){
            //  db.mallocFailed = 1;
            //  return 0;
            //}
            //Print.Snprintf(n, zStmt,"CREATE TABLE ");
            zStmt.Append("CREATE TABLE ");
            k = Utility.Sqlite3Strlen30(zStmt);
            identPut(zStmt, ref k, p.zName);
            zStmt.Append('('); //zStmt[k++] = '(';
            for (i = 0; i < p.nCol; i++)
            {
//, pCol++){
                pCol = p.aCol[i];
                var azType = new[]
                                 {
/* ColumnAffinityType.SQLITE_AFF_TEXT    */ " TEXT",
/* ColumnAffinityType.SQLITE_AFF_NONE    */ "",
/* ColumnAffinityType.SQLITE_AFF_NUMERIC */ " NUM",
/* ColumnAffinityType.SQLITE_AFF_INTEGER */ " INT",
/* ColumnAffinityType.SQLITE_AFF_REAL    */ " REAL"
                                 };
                int len;
                string zType;

                zStmt.Append(zSep); //  Print.Snprintf(n-k, zStmt[k], zSep);
                k = Utility.Sqlite3Strlen30(zStmt); //  k += strlen(zStmt[k]);
                zSep = zSep2;
                identPut(zStmt, ref k, pCol.zName);
                Debug.Assert(pCol.affinity - ColumnAffinityType.SQLITE_AFF_TEXT >= 0);
                Debug.Assert(pCol.affinity - ColumnAffinityType.SQLITE_AFF_TEXT < azType.Length); //sizeof(azType)/sizeof(azType[0]) );
                UnitTest.TestCase(pCol.affinity == ColumnAffinityType.SQLITE_AFF_TEXT);
                UnitTest.TestCase(pCol.affinity == ColumnAffinityType.SQLITE_AFF_NONE);
                UnitTest.TestCase(pCol.affinity == ColumnAffinityType.SQLITE_AFF_NUMERIC);
                UnitTest.TestCase(pCol.affinity == ColumnAffinityType.SQLITE_AFF_INTEGER);
                UnitTest.TestCase(pCol.affinity == ColumnAffinityType.SQLITE_AFF_REAL);

                zType = azType[pCol.affinity - ColumnAffinityType.SQLITE_AFF_TEXT];
                len = Utility.Sqlite3Strlen30(zType);
                Debug.Assert(pCol.affinity == ColumnAffinityType.SQLITE_AFF_NONE
                             || pCol.affinity == AffinityType(zType));
                zStmt.Append(zType); // memcpy( &zStmt[k], zType, len );
                k += len;
                Debug.Assert(k <= n);
            }
            zStmt.Append(zEnd); //Print.Snprintf(n-k, zStmt[k], "%s", zEnd);
            return zStmt.ToString();
        }

        /*
    ** This routine is called to report the final ")" that terminates
    ** a CREATE TABLE statement.
    **
    ** The table structure that other action routines have been building
    ** is added to the internal hash tables, assuming no errors have
    ** occurred.
    **
    ** An entry for the table is made in the master table on disk, unless
    ** this is a temporary table or db.init.busy==1.  When db.init.busy==1
    ** it means we are reading the sqlite_master table because we just
    ** connected to the database or because the sqlite_master table has
    ** recently changed, so the entry for this table already exists in
    ** the sqlite_master table.  We do not want to create it again.
    **
    ** If the pSelect argument is not NULL, it means that this routine
    ** was called to create a table generated from a
    ** "CREATE TABLE ... AS SELECT ..." statement.  The column names of
    ** the new table will match the result set of the SELECT.
    */
        // OVERLOADS, so I don't need to rewrite parse.c
        public static void EndTable(Parse pParse, Token pCons, Token pEnd, int null_4)
        {
            EndTable(pParse, pCons, pEnd, null);
        }

        public static void EndTable(Parse pParse, int null_2, int null_3, Select pSelect)
        {
            EndTable(pParse, null, null, pSelect);
        }

        public static void EndTable(
            Parse pParse, /* Parse context */
            Token pCons, /* The ',' token after the last column defn. */
            Token pEnd, /* The final ')' token in the CREATE TABLE */
            Select pSelect /* Select from a "CREATE ... AS SELECT" */
            )
        {
            Table p;
            sqlite3 db = pParse.db;
            int iDb;

            if ((pEnd == null && pSelect == null) /*|| db.mallocFailed != 0 */)
            {
                return;
            }
            p = pParse.pNewTable;
            if (p == null) return;

            Debug.Assert(0 == db.init.busy || pSelect == null);

            iDb = sqlite3SchemaToIndex(db, p.pSchema);

#if !SQLITE_OMIT_CHECK
            /* Resolve names in all CHECK constraint expressions.
*/
            if (p.pCheck != null)
            {
                SrcList sSrc; /* Fake SrcList for pParse.pNewTable */
                NameContext sNC; /* Name context for pParse.pNewTable */

                sNC = new NameContext(); // memset(sNC, 0, sizeof(sNC));
                sSrc = new SrcList(); // memset(sSrc, 0, sizeof(sSrc));
                sSrc.nSrc = 1;
                sSrc.a = new SrcList_item[1];
                sSrc.a[0] = new SrcList_item();
                sSrc.a[0].zName = p.zName;
                sSrc.a[0].pTab = p;
                sSrc.a[0].iCursor = -1;
                sNC.pParse = pParse;
                sNC.pSrcList = sSrc;
                sNC.isCheck = 1;
                if (sqlite3ResolveExprNames(sNC, ref p.pCheck) != 0)
                {
                    return;
                }
            }
#endif
            // * !SQLITE_OMIT_CHECK) */

            /* If the db.init.busy is 1 it means we are reading the SQL off the
** "sqlite_master" or "sqlite_temp_master" table on the disk.
** So do not write to the disk again.  Extract the root page number
** for the table from the db.init.newTnum field.  (The page number
** should have been put there by the sqliteOpenCb routine.)
*/
            if (db.init.busy != 0)
            {
                p.tnum = db.init.newTnum;
            }

            /* If not initializing, then create a record for the new table
      ** in the SQLITE_MASTER table of the database.
      **
      ** If this is a TEMPORARY table, write the entry into the auxiliary
      ** file instead of into the main database file.
      */
            if (0 == db.init.busy)
            {
                int n;
                Vdbe v;
                String zType = ""; /* "view" or "table" */
                String zType2 = ""; /* "VIEW" or "TABLE" */
                String zStmt = ""; /* Text of the CREATE TABLE or CREATE VIEW statement */

                v = SelectHelper.GetVdbe(pParse);
                if (UnitTest.NEVER(v == null)) return;

                VdbeAux.VdbeAddOp1(v, OPCode.OP_Close, 0);

                /*
        ** Initialize zType for the new view or table.
        */
                if (p.pSelect == null)
                {
                    /* A regular table */
                    zType = "table";
                    zType2 = "TABLE";
#if !SQLITE_OMIT_VIEW
                }
                else
                {
                    /* A view */
                    zType = "view";
                    zType2 = "VIEW";
#endif
                }

                /* If this is a CREATE TABLE xx AS SELECT ..., execute the SELECT
        ** statement to populate the new table. The root-page number for the
        ** new table is in register pParse->regRoot.
        **
        ** Once the SELECT has been coded by sqlite3Select(), it is in a
        ** suitable state to query for the column names and types to be used
        ** by the new table.
        **
        ** A shared-cache write-lock is not required to write to the new table,
        ** as a schema-lock must have already been obtained to create it. Since
        ** a schema-lock excludes all other database users, the write-lock would
        ** be redundant.
        */
                if (pSelect != null)
                {
                    var dest = new SelectDest();
                    Table pSelTab;

                    Debug.Assert(pParse.nTab == 1);
                    VdbeAux.VdbeAddOp3(v, OPCode.OP_OpenWrite, 1, pParse.regRoot, iDb);
                    VdbeAux.VdbeChangeP5(v, 1);
                    pParse.nTab = 2;
                    sqlite3SelectDestInit(dest, SelectResultType.SRT_Table, 1);
                    sqlite3Select(pParse, pSelect, ref dest);
                    VdbeAux.VdbeAddOp1(v, OPCode.OP_Close, 1);
                    if (pParse.nErr == 0)
                    {
                        pSelTab = sqlite3ResultSetOfSelect(pParse, pSelect);
                        if (pSelTab == null) return;
                        Debug.Assert(p.aCol == null);
                        p.nCol = pSelTab.nCol;
                        p.aCol = pSelTab.aCol;
                        pSelTab.nCol = 0;
                        pSelTab.aCol = null;
                        DeleteTable(ref pSelTab);
                    }
                }

                /* Compute the complete text of the CREATE statement */
                if (pSelect != null)
                {
                    zStmt = createTableStmt(db, p);
                }
                else
                {
                    n = (pParse.sNameToken.z.Length - pEnd.z.Length) + 1;
                    zStmt = Print.MPrintf(db,
                                           "CREATE %s %.*s", zType2, n, pParse.sNameToken.z
                        );
                }

                /* A slot for the record has already been allocated in the
        ** SQLITE_MASTER table.  We just need to update that slot with all
        ** the information we've collected.
        */
                NestedParse(pParse,
                                   "UPDATE %Q.%s " +
                                   "SET type='%s', name=%Q, tbl_name=%Q, rootpage=#%d, sql=%Q " +
                                   "WHERE rowid=#%d",
                                   db.aDb[iDb].zName, Helper.SchemaTable(iDb),
                                   zType,
                                   p.zName,
                                   p.zName,
                                   pParse.regRoot,
                                   zStmt,
                                   pParse.regRowid
                    );
                MemPool.DbFree(db, ref zStmt);
                ChangeCookie(pParse, iDb);

#if !SQLITE_OMIT_AUTOINCREMENT
                /* Check to see if we need to create an sqlite_sequence table for
** keeping track of autoincrement keys.
*/
                if ((p.tabFlags & TabFlag.TF_Autoincrement) != 0)
                {
                    Db pDb = db.aDb[iDb];
                    if (pDb.pSchema.pSeqTab == null)
                    {
                        NestedParse(pParse,
                                           "CREATE TABLE %Q.sqlite_sequence(name,seq)",
                                           pDb.zName
                            );
                    }
                }
#endif

                /* Reparse everything to update our internal data structures */
                VdbeAux.VdbeAddOp4(v, OPCode.OP_ParseSchema, iDb, 0, 0,
                                  Print.MPrintf(db, "tbl_name='%q'", p.zName), P4Type.P4_DYNAMIC);
            }


            /* Add the table to the in-memory representation of the database.
      */
            if (db.init.busy != 0)
            {
                Table pOld;
                Schema pSchema = p.pSchema;
                pOld = (Table) HashHelper.HashInsert(ref pSchema.tblHash, p.zName,
                                                 Utility.Sqlite3Strlen30(p.zName), p);
                if (pOld != null)
                {
                    Debug.Assert(p == pOld); /* Malloc must have failed inside HashInsert() */
                    //        db.mallocFailed = 1;
                    return;
                }
                pParse.pNewTable = null;
                db.nTable++;
                db.flags |= Flag.SQLITE_InternChanges;

#if !SQLITE_OMIT_ALTERTABLE
                if (p.pSelect == null)
                {
                    string zName = pParse.sNameToken.z;
                    int nName;
                    Debug.Assert(pSelect == null && pCons != null && pEnd != null);
                    if (pCons.z == null)
                    {
                        pCons = pEnd;
                    }
                    nName = zName.Length - pCons.z.Length;
                    p.addColOffset = 13 + nName; // UtfHelper.Utf8CharLen(zName, nName);
                }
#endif
            }
        }

#if !SQLITE_OMIT_VIEW
        /*
** The parser calls this routine in order to create a new VIEW
*/

        public static void CreateView(
            Parse pParse, /* The parsing context */
            Token pBegin, /* The CREATE token that begins the statement */
            Token pName1, /* The token that holds the name of the view */
            Token pName2, /* The token that holds the name of the view */
            Select pSelect, /* A SELECT statement that will become the new view */
            int isTemp, /* TRUE for a TEMPORARY view */
            int noErr /* Suppress error messages if VIEW already exists */
            )
        {
            Table p;
            int n;
            string z; //const char *z;
            Token sEnd;
            var sFix = new DbFixer();
            Token pName = null;
            int iDb;
            sqlite3 db = pParse.db;

            if (pParse.nVar > 0)
            {
                Utility.Sqlite3ErrorMsg(pParse, "parameters are not allowed in views");
                sqlite3SelectDelete(db, ref pSelect);
                return;
            }
            StartTable(pParse, pName1, pName2, isTemp, 1, 0, noErr);
            p = pParse.pNewTable;
            if (p == null)
            {
                sqlite3SelectDelete(db, ref pSelect);
                return;
            }
            Debug.Assert(pParse.nErr == 0);
                /* If Build.StartTable return non-NULL then
** there could not have been an error */
            TwoPartName(pParse, pName1, pName2, ref pName);
            iDb = sqlite3SchemaToIndex(db, p.pSchema);
            if (AttachHelper.FixInit(sFix, pParse, iDb, "view", pName) != 0
                && AttachHelper.FixSelect(sFix, pSelect) != 0
                )
            {
                sqlite3SelectDelete(db, ref pSelect);
                return;
            }

            /* Make a copy of the entire SELECT statement that defines the view.
      ** This will force all the Expr.token.z values to be dynamically
      ** allocated rather than point to the input string - which means that
      ** they will persist after the current sqlite3_exec() call returns.
      */
            p.pSelect = ExprHelper.SelectDup(db, pSelect, Const.EXPRDUP_REDUCE);
            sqlite3SelectDelete(db, ref pSelect);
            //if ( db.mallocFailed != 0 )
            //{
            //  return;
            //}
            if (0 == db.init.busy)
            {
                ViewGetColumnNames(pParse, p);
            }

            /* Locate the end of the CREATE VIEW statement.  Make sEnd point to
      ** the end.
      */
            sEnd = pParse.sLastToken;
            if (UnitTest.ALWAYS(sEnd.z[0] != 0) && sEnd.z[0] != ';')
            {
                sEnd.z = sEnd.z.Substring(sEnd.n);
            }
            sEnd.n = 0;
            n = (pBegin.z.Length - sEnd.z.Length); //sEnd.z - pBegin.z;
            z = pBegin.z;
            while (UnitTest.ALWAYS(n > 0) && SqliteInt.Sqlite3Isspace(z[n - 1]))
            {
                n--;
            }
            sEnd.z = z.Substring(n - 1);
            sEnd.n = 1;

            /* Use Build.EndTable() to add the view to the SQLITE_MASTER table */
            EndTable(pParse, null, sEnd, null);
            return;
        }
#endif
        // * SQLITE_OMIT_VIEW */

#if !SQLITE_OMIT_VIEW || !SQLITE_OMIT_VIRTUALTABLE
        /*
** The Table structure pTable is really a VIEW.  Fill in the names of
** the columns of the view in the pTable structure.  Return the number
** of errors.  If an error is seen leave an error message in pParse.zErrMsg.
*/

        public static int ViewGetColumnNames(Parse pParse, Table pTable)
        {
            Table pSelTab; /* A fake table from which we get the result set */
            Select pSel; /* Copy of the SELECT that implements the view */
            int nErr = 0; /* Number of errors encountered */
            int n; /* Temporarily holds the number of cursors assigned */
            sqlite3 db = pParse.db; /* Database connection for malloc errors */
            dxAuth xAuth; //)(void*,int,const char*,const char*,const char*,const char*);

            Debug.Assert(pTable != null);

#if !SQLITE_OMIT_VIRTUALTABLE
if ( sqlite3VtabCallConnect( pParse, pTable ) )
{
return StatusCode.SQLITE_ERROR;
}
#endif
            if (Utility.IsVirtual(pTable)) return 0;

#if !SQLITE_OMIT_VIEW
            /* A positive nCol means the columns names for this view are
** already known.
*/
            if (pTable.nCol > 0) return 0;

            /* A negative nCol is a special marker meaning that we are currently
      ** trying to compute the column names.  If we enter this routine with
      ** a negative nCol, it means two or more views form a loop, like this:
      **
      **     CREATE VIEW one AS SELECT * FROM two;
      **     CREATE VIEW two AS SELECT * FROM one;
      **
      ** Actually, the error above is now caught prior to reaching this point.
      ** But the following test is still important as it does come up
      ** in the following:
      **
      **     CREATE TABLE main.ex1(a);
      **     CREATE TEMP VIEW ex1 AS SELECT a FROM ex1;
      **     SELECT * FROM temp.ex1;
      */
            if (pTable.nCol < 0)
            {
                Utility.Sqlite3ErrorMsg(pParse, "view %s is circularly defined", pTable.zName);
                return 1;
            }
            Debug.Assert(pTable.nCol >= 0);

            /* If we get this far, it means we need to compute the table names.
      ** Note that the call to sqlite3ResultSetOfSelect() will expand any
      ** "*" elements in the results set of the view and will assign cursors
      ** to the elements of the FROM clause.  But we do not want these changes
      ** to be permanent.  So the computation is done on a copy of the SELECT
      ** statement that defines the view.
      */
            Debug.Assert(pTable.pSelect != null);
            pSel = ExprHelper.SelectDup(db, pTable.pSelect, 0);
            if (pSel != null)
            {
                byte enableLookaside = db.lookaside.bEnabled;
                n = pParse.nTab;
                SrcListAssignCursors(pParse, pSel.pSrc);
                pTable.nCol = -1;
                db.lookaside.bEnabled = 0;
#if !SQLITE_OMIT_AUTHORIZATION
xAuth = db.xAuth;
db.xAuth = 0;
pSelTab = sqlite3ResultSetOfSelect(pParse, pSel);
db.xAuth = xAuth;
#else
                pSelTab = sqlite3ResultSetOfSelect(pParse, pSel);
#endif
                db.lookaside.bEnabled = enableLookaside;
                pParse.nTab = n;
                if (pSelTab != null)
                {
                    Debug.Assert(pTable.aCol == null);
                    pTable.nCol = pSelTab.nCol;
                    pTable.aCol = pSelTab.aCol;
                    pSelTab.nCol = 0;
                    pSelTab.aCol = null;
                    DeleteTable(ref pSelTab);
                    pTable.pSchema.flags |= DBSchemaFlag.DB_UnresetViews;
                }
                else
                {
                    pTable.nCol = 0;
                    nErr++;
                }
                sqlite3SelectDelete(db, ref pSel);
            }
            else
            {
                nErr++;
            }
#endif
            // * SQLITE_OMIT_VIEW */
            return nErr;
        }
#endif
        // * !SQLITE_OMIT_VIEW) || !SQLITE_OMIT_VIRTUALTABLE) */

#if !SQLITE_OMIT_VIEW
        /*
** Clear the column names from every VIEW in database idx.
*/

        private static void sqliteViewResetAll(sqlite3 db, int idx)
        {
            HashElem i;
            if (!Helper.DbHasProperty(db, idx, DBSchemaFlag.DB_UnresetViews)) return;
            //for(i=HashHelper.HashFirst(&db.aDb[idx].pSchema.tblHash); i;i=HashHelper.HashNext(i)){
            for (i = db.aDb[idx].pSchema.tblHash.first; i != null; i = i.next)
            {
                var pTab = (Table) i.data; // HashHelper.HashData( i );
                if (pTab.pSelect != null)
                {
                    ResetColumnNames(pTab);
                }
            }
            Helper.DbClearProperty(db, idx, DBSchemaFlag.DB_UnresetViews);
        }
#else
//# define sqliteViewResetAll(A,B)
#endif
        // * SQLITE_OMIT_VIEW */

        /*
** This function is called by the VDBE to adjust the internal schema
** used by SQLite when the btree layer moves a table root page. The
** root-page of a table or index in database iDb has changed from iFrom
** to iTo.
**
** Ticket #1728:  The symbol table might still contain information
** on tables and/or indices that are the process of being deleted.
** If you are unlucky, one of those deleted indices or tables might
** have the same rootpage number as the real table or index that is
** being moved.  So we cannot stop searching after the first match
** because the first match might be for one of the deleted indices
** or tables and not the table/index that is actually being moved.
** We must continue looping until all tables and indices with
** rootpage==iFrom have been converted to have a rootpage of iTo
** in order to be certain that we got the right one.
*/
#if !SQLITE_OMIT_AUTOVACUUM
        public static void RootPageMoved(Db pDb, int iFrom, int iTo)
        {
            HashElem pElem;
            Hash pHash;

            pHash = pDb.pSchema.tblHash;
            for (pElem = pHash.first; pElem != null; pElem = pElem.next)
                // ( pElem = HashHelper.HashFirst( pHash ) ; pElem ; pElem = HashHelper.HashNext( pElem ) )
            {
                var pTab = (Table) pElem.data; // HashHelper.HashData( pElem );
                if (pTab.tnum == iFrom)
                {
                    pTab.tnum = iTo;
                }
            }
            pHash = pDb.pSchema.idxHash;
            for (pElem = pHash.first; pElem != null; pElem = pElem.next)
                // ( pElem = HashHelper.HashFirst( pHash ) ; pElem ; pElem = HashHelper.HashNext( pElem ) )
            {
                var pIdx = (Index) pElem.data; // HashHelper.HashData( pElem );
                if (pIdx.tnum == iFrom)
                {
                    pIdx.tnum = iTo;
                }
            }
        }
#endif

        /*
** Write code to erase the table with root-page iTable from database iDb.
** Also write code to modify the sqlite_master table and internal schema
** if a root-page of another table is moved by the btree-layer whilst
** erasing iTable (this can happen with an auto-vacuum database).
*/

        private static void destroyRootPage(Parse pParse, int iTable, int iDb)
        {
            Vdbe v = SelectHelper.GetVdbe(pParse);
            int r1 = ExprHelper.GetTempReg(pParse);
            VdbeAux.VdbeAddOp3(v, OPCode.OP_Destroy, iTable, r1, iDb);
            MayAbort(pParse);
#if !SQLITE_OMIT_AUTOVACUUM
            /* OPCode.OP_Destroy stores an in integer r1. If this integer
** is non-zero, then it is the root page number of a table moved to
** location iTable. The following code modifies the sqlite_master table to
** reflect this.
**
** The "#NNN" in the SQL is a special constant that means whatever value
** is in register NNN.  See grammar rules associated with the TokenKeyword.TK_REGISTER
** token for additional information.
*/
            NestedParse(pParse,
                               "UPDATE %Q.%s SET rootpage=%d WHERE #%d AND rootpage=#%d",
                               pParse.db.aDb[iDb].zName, Helper.SchemaTable(iDb), iTable, r1, r1);
#endif
            ExprHelper.ReleaseTempReg(pParse, r1);
        }

        /*
    ** Write VDBE code to erase table pTab and all associated indices on disk.
    ** Code to update the sqlite_master tables and internal schema definitions
    ** in case a root-page belonging to another table is moved by the btree layer
    ** is also added (this can happen with an auto-vacuum database).
    */

        private static void destroyTable(Parse pParse, Table pTab)
        {
#if  SQLITE_OMIT_AUTOVACUUM
Index pIdx;
int iDb = sqlite3SchemaToIndex( pParse.db, pTab.pSchema );
destroyRootPage( pParse, pTab.tnum, iDb );
for ( pIdx = pTab.pIndex ; pIdx != null ; pIdx = pIdx.pNext )
{
destroyRootPage( pParse, pIdx.tnum, iDb );
}
#else
            /* If the database may be auto-vacuum capable (if SQLITE_OMIT_AUTOVACUUM
** is not defined), then it is important to call OPCode.OP_Destroy on the
** table and index root-pages in order, starting with the numerically
** largest root-page number. This guarantees that none of the root-pages
** to be destroyed is relocated by an earlier OPCode.OP_Destroy. i.e. if the
** following were coded:
**
** OPCode.OP_Destroy 4 0
** ...
** OPCode.OP_Destroy 5 0
**
** and root page 5 happened to be the largest root-page number in the
** database, then root page 5 would be moved to page 4 by the
** "OPCode.OP_Destroy 4 0" opcode. The subsequent "OPCode.OP_Destroy 5 0" would hit
** a free-list page.
*/
            int iTab = pTab.tnum;
            int iDestroyed = 0;

            while (true)
            {
                Index pIdx;
                int iLargest = 0;

                if (iDestroyed == 0 || iTab < iDestroyed)
                {
                    iLargest = iTab;
                }
                for (pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext)
                {
                    int iIdx = pIdx.tnum;
                    Debug.Assert(pIdx.pSchema == pTab.pSchema);
                    if ((iDestroyed == 0 || (iIdx < iDestroyed)) && iIdx > iLargest)
                    {
                        iLargest = iIdx;
                    }
                }
                if (iLargest == 0)
                {
                    return;
                }
                else
                {
                    int iDb = sqlite3SchemaToIndex(pParse.db, pTab.pSchema);
                    destroyRootPage(pParse, iLargest, iDb);
                    iDestroyed = iLargest;
                }
            }
#endif
        }

        /*
    ** This routine is called to do the work of a DROP TABLE statement.
    ** pName is the name of the table to be dropped.
    */

        public static void DropTable(Parse pParse, SrcList pName, int isView, int noErr)
        {
            Table pTab;
            Vdbe v;
            sqlite3 db = pParse.db;
            int iDb;

            //if ( db.mallocFailed != 0 )
            //{
            //  goto exit_drop_table;
            //}
            Debug.Assert(pParse.nErr == 0);
            Debug.Assert(pName.nSrc == 1);
            if (noErr != 0) db.suppressErr++;
            pTab = LocateTable(pParse, isView,
                                      pName.a[0].zName, pName.a[0].zDatabase);
            if (noErr != 0) db.suppressErr--;

            if (pTab == null)
            {
                goto exit_drop_table;
            }
            iDb = sqlite3SchemaToIndex(db, pTab.pSchema);
            Debug.Assert(iDb >= 0 && iDb < db.nDb);

            /* If pTab is a virtual table, call ViewGetColumnNames() to ensure
      ** it is initialized.
      */
            if (Utility.IsVirtual(pTab) && ViewGetColumnNames(pParse, pTab) != 0)
            {
                goto exit_drop_table;
            }
#if !SQLITE_OMIT_AUTHORIZATION
{
int code;
string zTab = Helper.SchemaTable(iDb);
string zDb = db.aDb[iDb].zName;
string zArg2 = 0;
if( sqlite3AuthCheck(pParse, ActionCode.SQLITE_DELETE, zTab, 0, zDb)){
goto exit_drop_table;
}
if( isView ){
if( Const.OMIT_TEMPDB ==0&& iDb==1 ){
code = SQLITE_DROPCode.OP_TEMP_VIEW;
}else{
code = SQLITE_DROPCode.OP_VIEW;
}
}else if( Utility.IsVirtual(pTab) ){
code = SQLITE_DROPCode.OP_VTABLE;
zArg2 = sqlite3GetVTable(db, pTab)->pMod->zName;
}else{
if( Const.OMIT_TEMPDB ==0&& iDb==1 ){
code = SQLITE_DROPCode.OP_TEMP_TABLE;
}else{
code = SQLITE_DROPCode.OP_TABLE;
}
}
if( sqlite3AuthCheck(pParse, code, pTab.zName, zArg2, zDb) ){
goto exit_drop_table;
}
if( sqlite3AuthCheck(pParse, ActionCode.SQLITE_DELETE, pTab.zName, 0, zDb) ){
goto exit_drop_table;
}
}
#endif
            if (Utility.Sqlite3StrNICmp(pTab.zName, "sqlite_", 7) == 0)
            {
                Utility.Sqlite3ErrorMsg(pParse, "table %s may not be dropped", pTab.zName);
                goto exit_drop_table;
            }

#if !SQLITE_OMIT_VIEW
            /* Ensure DROP TABLE is not used on a view, and DROP VIEW is not used
** on a table.
*/
            if (isView != 0 && pTab.pSelect == null)
            {
                Utility.Sqlite3ErrorMsg(pParse, "use DROP TABLE to delete table %s", pTab.zName);
                goto exit_drop_table;
            }
            if (0 == isView && pTab.pSelect != null)
            {
                Utility.Sqlite3ErrorMsg(pParse, "use DROP VIEW to delete view %s", pTab.zName);
                goto exit_drop_table;
            }
#endif

            /* Generate code to remove the table from the master table
** on disk.
*/
            v = SelectHelper.GetVdbe(pParse);
            if (v != null)
            {
                Trigger pTrigger;
                Db pDb = db.aDb[iDb];
                BeginWriteOperation(pParse, 1, iDb);

#if !SQLITE_OMIT_VIRTUALTABLE
        if ( Utility.IsVirtual( pTab ) )
        {
          VdbeAux.VdbeAddOp0( v, OPCode.OP_VBegin );
        }
#endif
                sqlite3FkDropTable(pParse, pName, pTab);

                /* Drop all triggers associated with the table being dropped. Code
        ** is generated to remove entries from sqlite_master and/or
        ** sqlite_temp_master if required.
        */
                pTrigger = sqlite3TriggerList(pParse, pTab);
                while (pTrigger != null)
                {
                    Debug.Assert(pTrigger.pSchema == pTab.pSchema ||
                                 pTrigger.pSchema == db.aDb[1].pSchema);
                    sqlite3DropTriggerPtr(pParse, pTrigger);
                    pTrigger = pTrigger.pNext;
                }

#if !SQLITE_OMIT_AUTOINCREMENT
                /* Remove any entries of the sqlite_sequence table associated with
** the table being dropped. This is done before the table is dropped
** at the btree level, in case the sqlite_sequence table needs to
** move as a result of the drop (can happen in auto-vacuum mode).
*/
                if ((pTab.tabFlags & TabFlag.TF_Autoincrement) != 0)
                {
                    NestedParse(pParse,
                                       "DELETE FROM %s.sqlite_sequence WHERE name=%Q",
                                       pDb.zName, pTab.zName
                        );
                }
#endif

                /* Drop all SQLITE_MASTER table and index entries that refer to the
** table. The program name loops through the master table and deletes
** every row that refers to a table of the same name as the one being
** dropped. Triggers are handled seperately because a trigger can be
** created in the temp database that refers to a table in another
** database.
*/
                NestedParse(pParse,
                                   "DELETE FROM %Q.%s WHERE tbl_name=%Q and type!='trigger'",
                                   pDb.zName, Helper.SchemaTable(iDb), pTab.zName);

                /* Drop any statistics from the sqlite_stat1 table, if it exists */
                if (FindTable(db, "sqlite_stat1", db.aDb[iDb].zName) != null)
                {
                    NestedParse(pParse,
                                       "DELETE FROM %Q.sqlite_stat1 WHERE tbl=%Q", pDb.zName, pTab.zName
                        );
                }

                if (0 == isView && !Utility.IsVirtual(pTab))
                {
                    destroyTable(pParse, pTab);
                }

                /* Remove the table entry from SQLite's internal schema and modify
        ** the schema cookie.
        */
                if (Utility.IsVirtual(pTab))
                {
                    VdbeAux.VdbeAddOp4(v, OPCode.OP_VDestroy, iDb, 0, 0, pTab.zName, 0);
                }
                VdbeAux.VdbeAddOp4(v, OPCode.OP_DropTable, iDb, 0, 0, pTab.zName, 0);
                ChangeCookie(pParse, iDb);
            }
            sqliteViewResetAll(db, iDb);

            exit_drop_table:
            SrcListDelete(db, ref pName);
        }

        /*
    ** This routine is called to create a new foreign key on the table
    ** currently under construction.  pFromCol determines which columns
    ** in the current table point to the foreign key.  If pFromCol==0 then
    ** connect the key to the last column inserted.  pTo is the name of
    ** the table referred to.  pToCol is a list of tables in the other
    ** pTo table that the foreign key points to.  flags contains all
    ** information about the conflict resolution algorithms specified
    ** in the ON DELETE, ON UPDATE and ON INSERT clauses.
    **
    ** An FKey structure is created and added to the table currently
    ** under construction in the pParse.pNewTable field.
    **
    ** The foreign key is set for IMMEDIATE processing.  A subsequent call
    ** to Build.DeferForeignKey() might change this to DEFERRED.
    */
        // OVERLOADS, so I don't need to rewrite parse.c
        public static void CreateForeignKey(Parse pParse, int null_2, Token pTo, ExprList pToCol, int flags)
        {
            CreateForeignKey(pParse, null, pTo, pToCol, flags);
        }

        public static void CreateForeignKey(
            Parse pParse, /* Parsing context */
            ExprList pFromCol, /* Columns in this table that point to other table */
            Token pTo, /* Name of the other table */
            ExprList pToCol, /* Columns in the other table */
            int flags /* Conflict resolution algorithms. */
            )
        {
            sqlite3 db = pParse.db;
#if !SQLITE_OMIT_FOREIGN_KEY
            FKey pFKey = null;
            FKey pNextTo;
            Table p = pParse.pNewTable;
            int nByte;
            int i;
            int nCol;
            //string z;

            Debug.Assert(pTo != null);
            if (p == null || Const.IN_DECLARE_VTAB) goto fk_end;
            if (pFromCol == null)
            {
                int iCol = p.nCol - 1;
                if (UnitTest.NEVER(iCol < 0)) goto fk_end;
                if (pToCol != null && pToCol.nExpr != 1)
                {
                    Utility.Sqlite3ErrorMsg(pParse, "foreign key on %s" +
                                            " should reference only one column of table %T",
                                    p.aCol[iCol].zName, pTo);
                    goto fk_end;
                }
                nCol = 1;
            }
            else if (pToCol != null && pToCol.nExpr != pFromCol.nExpr)
            {
                Utility.Sqlite3ErrorMsg(pParse,
                                "number of columns in foreign key does not match the number of " +
                                "columns in the referenced table");
                goto fk_end;
            }
            else
            {
                nCol = pFromCol.nExpr;
            }
            //nByte = sizeof(*pFKey) + (nCol-1)*sizeof(pFKey.aCol[0]) + pTo.n + 1;
            //if( pToCol ){
            //  for(i=0; i<pToCol.nExpr; i++){
            //    nByte += Utility.Sqlite3Strlen30(pToCol->a[i].zName) + 1;
            //  }
            //}
            pFKey = new FKey(); //Malloc.DbMallocZero(db, nByte );
            if (pFKey == null)
            {
                goto fk_end;
            }
            pFKey.pFrom = p;
            pFKey.pNextFrom = p.pFKey;
            //z = pFKey.aCol[nCol].zCol;
            pFKey.aCol = new FKey.sColMap[nCol]; // z;
            pFKey.aCol[0] = new FKey.sColMap();
            pFKey.zTo = pTo.z.Substring(0, pTo.n); //memcpy( z, pTo.z, pTo.n );
            //z[pTo.n] = 0;
            Utility.Sqlite3Dequote(ref pFKey.zTo);
            //z += pTo.n + 1;
            pFKey.nCol = nCol;
            if (pFromCol == null)
            {
                pFKey.aCol[0].iFrom = p.nCol - 1;
            }
            else
            {
                for (i = 0; i < nCol; i++)
                {
                    if (pFKey.aCol[i] == null) pFKey.aCol[i] = new FKey.sColMap();
                    int j;
                    for (j = 0; j < p.nCol; j++)
                    {
                        if (Utility.Sqlite3StrICmp(p.aCol[j].zName, pFromCol.a[i].zName) == 0)
                        {
                            pFKey.aCol[i].iFrom = j;
                            break;
                        }
                    }
                    if (j >= p.nCol)
                    {
                        Utility.Sqlite3ErrorMsg(pParse,
                                        "unknown column \"%s\" in foreign key definition",
                                        pFromCol.a[i].zName);
                        goto fk_end;
                    }
                }
            }
            if (pToCol != null)
            {
                for (i = 0; i < nCol; i++)
                {
                    int n = Utility.Sqlite3Strlen30(pToCol.a[i].zName);
                    if (pFKey.aCol[i] == null) pFKey.aCol[i] = new FKey.sColMap();
                    pFKey.aCol[i].zCol = pToCol.a[i].zName;
                    //memcpy( z, pToCol.a[i].zName, n );
                    //z[n] = 0;
                    //z += n + 1;
                }
            }
            pFKey.isDeferred = 0;
            pFKey.aAction[0] = (byte) (flags & 0xff); /* ON DELETE action */
            pFKey.aAction[1] = (byte) ((flags >> 8) & 0xff); /* ON UPDATE action */

            pNextTo = (FKey) HashHelper.HashInsert(ref p.pSchema.fkeyHash,
                                               pFKey.zTo, Utility.Sqlite3Strlen30(pFKey.zTo), pFKey
                                 );
            //if( pNextTo==pFKey ){
            //  db.mallocFailed = 1;
            //  goto fk_end;
            //}
            if (pNextTo != null)
            {
                Debug.Assert(pNextTo.pPrevTo == null);
                pFKey.pNextTo = pNextTo;
                pNextTo.pPrevTo = pFKey;
            }
            /* Link the foreign key to the table as the last step.
      */
            p.pFKey = pFKey;
            pFKey = null;

            fk_end:
            MemPool.DbFree(db, ref pFKey);
#endif
            // * !SQLITE_OMIT_FOREIGN_KEY) */
            ExprHelper.ExprListDelete(db, ref pFromCol);
            ExprHelper.ExprListDelete(db, ref pToCol);
        }

        /*
    ** This routine is called when an INITIALLY IMMEDIATE or INITIALLY DEFERRED
    ** clause is seen as part of a foreign key definition.  The isDeferred
    ** parameter is 1 for INITIALLY DEFERRED and 0 for INITIALLY IMMEDIATE.
    ** The behavior of the most recently created foreign key is adjusted
    ** accordingly.
    */

        public static void DeferForeignKey(Parse pParse, int isDeferred)
        {
#if !SQLITE_OMIT_FOREIGN_KEY
            Table pTab;
            FKey pFKey;
            if ((pTab = pParse.pNewTable) == null || (pFKey = pTab.pFKey) == null) return;
            Debug.Assert(isDeferred == 0 || isDeferred == 1); /* EV: R-30323-21917 */
            pFKey.isDeferred = (byte) isDeferred;
#endif
        }

        /*
    ** Generate code that will erase and refill index pIdx.  This is
    ** used to initialize a newly created index or to recompute the
    ** content of an index in response to a REINDEX command.
    **
    ** if memRootPage is not negative, it means that the index is newly
    ** created.  The register specified by memRootPage contains the
    ** root page number of the index.  If memRootPage is negative, then
    ** the index already exists and must be cleared before being refilled and
    ** the root page number of the index is taken from pIndex.tnum.
    */

        public static void RefillIndex(Parse pParse, Index pIndex, int memRootPage)
        {
            Table pTab = pIndex.pTable; /* The table that is indexed */
            int iTab = pParse.nTab++; /* Btree cursor used for pTab */
            int iIdx = pParse.nTab++; /* Btree cursor used for pIndex */
            int addr1; /* Address of top of loop */
            int tnum; /* Root page of index */
            Vdbe v; /* Generate code into this virtual machine */
            KeyInfo pKey; /* KeyInfo for index */
            int regIdxKey; /* Registers containing the index key */
            int regRecord; /* Register holding assemblied index record */
            sqlite3 db = pParse.db; /* The database connection */
            int iDb = sqlite3SchemaToIndex(db, pIndex.pSchema);

#if !SQLITE_OMIT_AUTHORIZATION
if( sqlite3AuthCheck(pParse, ActionCode.SQLITE_REINDEX, pIndex.zName, 0,
db.aDb[iDb].zName ) ){
return;
}
#endif

            /* Require a write-lock on the table to perform this operation */
            sqlite3TableLock(pParse, iDb, pTab.tnum, 1, pTab.zName);
            v = SelectHelper.GetVdbe(pParse);
            if (v == null) return;
            if (memRootPage >= 0)
            {
                tnum = memRootPage;
            }
            else
            {
                tnum = pIndex.tnum;
                VdbeAux.VdbeAddOp2(v, OPCode.OP_Clear, tnum, iDb);
            }
            pKey = IndexKeyinfo(pParse, pIndex);
            VdbeAux.VdbeAddOp4(v, OPCode.OP_OpenWrite, iIdx, tnum, iDb,
                              pKey, P4Type.P4_KEYINFO_HANDOFF);
            if (memRootPage >= 0)
            {
                VdbeAux.VdbeChangeP5(v, 1);
            }
            sqlite3OpenTable(pParse, iTab, iDb, pTab, OPCode.OP_OpenRead);
            addr1 = VdbeAux.VdbeAddOp2(v, OPCode.OP_Rewind, iTab, 0);
            regRecord = ExprHelper.GetTempReg(pParse);
            regIdxKey = sqlite3GenerateIndexKey(pParse, pIndex, iTab, regRecord, true);
            if (pIndex.onError != OnConstraintError.OE_None)
            {
                int regRowid = regIdxKey + pIndex.nColumn;
                int j2 = VdbeAux.VdbeCurrentAddr(v) + 2;
                int pRegKey = regIdxKey; // SQLITE_INT_TO_PTR( regIdxKey );

                /* The registers accessed by the OPCode.OP_IsUnique opcode were allocated
        ** using ExprHelper.GetTempRange() inside of the sqlite3GenerateIndexKey()
        ** call above. Just before that function was freed they were released
        ** (made available to the compiler for reuse) using
        ** ExprHelper.ReleaseTempRange(). So in some ways having the OPCode.OP_IsUnique
        ** opcode use the values stored within seems dangerous. However, since
        ** we can be sure that no other temp registers have been allocated
        ** since ExprHelper.ReleaseTempRange() was called, it is safe to do so.
        */
                VdbeAux.VdbeAddOp4(v, OPCode.OP_IsUnique, iIdx, j2, regRowid, pRegKey, P4Type.P4_INT32);
                HaltConstraint(
                    pParse, OnConstraintError.OE_Abort, "indexed columns are not unique", P4Type.P4_STATIC);
            }
            VdbeAux.VdbeAddOp2(v, OPCode.OP_IdxInsert, iIdx, regRecord);
            VdbeAux.VdbeChangeP5(v, P5Value.OPFLAG_USESEEKRESULT);
            ExprHelper.ReleaseTempReg(pParse, regRecord);
            VdbeAux.VdbeAddOp2(v, OPCode.OP_Next, iTab, addr1 + 1);
            VdbeAux.VdbeJumpHere(v, addr1);
            VdbeAux.VdbeAddOp1(v, OPCode.OP_Close, iTab);
            VdbeAux.VdbeAddOp1(v, OPCode.OP_Close, iIdx);
        }

        /*
    ** Create a new index for an SQL table.  pName1.pName2 is the name of the index
    ** and pTblList is the name of the table that is to be indexed.  Both will
    ** be NULL for a primary key or an index that is created to satisfy a
    ** UNIQUE constraint.  If pTable and pIndex are NULL, use pParse.pNewTable
    ** as the table to be indexed.  pParse.pNewTable is a table that is
    ** currently being constructed by a CREATE TABLE statement.
    **
    ** pList is a list of columns to be indexed.  pList will be NULL if this
    ** is a primary key or unique-constraint on the most recent column added
    ** to the table currently under construction.
    **
    ** If the index is created successfully, return a pointer to the new Index
    ** structure. This is used by Build.AddPrimaryKey() to mark the index
    ** as the tables primary key (Index.autoIndex==2).
    */
        // OVERLOADS, so I don't need to rewrite parse.c
        public static Index CreateIndex(Parse pParse, int null_2, int null_3, int null_4, int null_5,
                                                int onError, int null_7, int null_8, int sortOrder, int ifNotExist)
        {
            return CreateIndex(pParse, null, null, null, null, onError, null, null, sortOrder, ifNotExist);
        }

        public static Index CreateIndex(Parse pParse, int null_2, int null_3, int null_4, ExprList pList,
                                                int onError, int null_7, int null_8, int sortOrder, int ifNotExist)
        {
            return CreateIndex(pParse, null, null, null, pList, onError, null, null, sortOrder, ifNotExist);
        }

        public static Index CreateIndex(
            Parse pParse, /* All information about this Parse */
            Token pName1, /* First part of index name. May be NULL */
            Token pName2, /* Second part of index name. May be NULL */
            SrcList pTblName, /* Table to index. Use pParse.pNewTable if 0 */
            ExprList pList, /* A list of columns to be indexed */
            int onError, /* OnConstraintError.OE_Abort, OnConstraintError.OE_Ignore, OnConstraintError.OE_Replace, or OnConstraintError.OE_None */
            Token pStart, /* The CREATE token that begins this statement */
            Token pEnd, /* The ")" that closes the CREATE INDEX statement */
            int sortOrder, /* Sort order of primary key when pList==NULL */
            int ifNotExist /* Omit error if index already exists */
            )
        {
            Index pRet = null; /* Pointer to return */
            Table pTab = null; /* Table to be indexed */
            Index pIndex = null; /* The index to be created */
            string zName = null; /* Name of the index */
            int nName; /* Number of characters in zName */
            int i, j;
            var nullId = new Token(); /* Fake token for an empty ID list */
            var sFix = new DbFixer(); /* For assigning database names to pTable */
            int sortOrderMask; /* 1 to honor DESC in index.  0 to ignore. */
            sqlite3 db = pParse.db;
            Db pDb; /* The specific table containing the indexed database */
            int iDb; /* Index of the database that is being written */
            Token pName = null; /* Unqualified name of the index to create */
            ExprList_item pListItem; /* For looping over pList */
            int nCol;
            int nExtra = 0;
            var zExtra = new StringBuilder();

            Debug.Assert(pStart == null || pEnd != null); /* pEnd must be non-NULL if pStart is */
            Debug.Assert(pParse.nErr == 0); /* Never called with prior errors */
            if ( /* db.mallocFailed != 0 || */ Const.IN_DECLARE_VTAB)
            {
                goto exit_create_index;
            }
            if (StatusCode.SQLITE_OK != Prepare.ReadSchema(pParse))
            {
                goto exit_create_index;
            }

            /*
      ** Find the table that is to be indexed.  Return early if not found.
      */
            if (pTblName != null)
            {
                /* Use the two-part index name to determine the database
        ** to search for the table. 'Fix' the table name to this db
        ** before looking up the table.
        */
                Debug.Assert(pName1 != null && pName2 != null);
                iDb = TwoPartName(pParse, pName1, pName2, ref pName);
                if (iDb < 0) goto exit_create_index;

#if !SQLITE_OMIT_TEMPDB
                /* If the index name was unqualified, check if the the table
** is a temp table. If so, set the database to 1. Do not do this
** if initialising a database schema.
*/
                if (0 == db.init.busy)
                {
                    pTab = sqlite3SrcListLookup(pParse, pTblName);
                    if (pName2.n == 0 && pTab != null && pTab.pSchema == db.aDb[1].pSchema)
                    {
                        iDb = 1;
                    }
                }
#endif

                if (AttachHelper.FixInit(sFix, pParse, iDb, "index", pName) != 0 &&
                    AttachHelper.FixSrcList(sFix, pTblName) != 0
                    )
                {
                    /* Because the parser constructs pTblName from a single identifier,
          ** AttachHelper.FixSrcList can never fail. */
                    Debugger.Break();
                }
                pTab = LocateTable(pParse, 0, pTblName.a[0].zName,
                                          pTblName.a[0].zDatabase);
                if (pTab == null /*|| db.mallocFailed != 0 */) goto exit_create_index;
                Debug.Assert(db.aDb[iDb].pSchema == pTab.pSchema);
            }
            else
            {
                Debug.Assert(pName == null);
                pTab = pParse.pNewTable;
                if (pTab == null) goto exit_create_index;
                iDb = sqlite3SchemaToIndex(db, pTab.pSchema);
            }
            pDb = db.aDb[iDb];

            Debug.Assert(pTab != null);
            Debug.Assert(pParse.nErr == 0);
            if (Utility.Sqlite3StrNICmp(pTab.zName, "sqlite_", 7) == 0
                && Utility.Sqlite3StrNICmp(pTab.zName, 7, "altertab_", 9) != 0)
            {
                Utility.Sqlite3ErrorMsg(pParse, "table %s may not be indexed", pTab.zName);
                goto exit_create_index;
            }
#if !SQLITE_OMIT_VIEW
            if (pTab.pSelect != null)
            {
                Utility.Sqlite3ErrorMsg(pParse, "views may not be indexed");
                goto exit_create_index;
            }
#endif
            if (Utility.IsVirtual(pTab))
            {
                Utility.Sqlite3ErrorMsg(pParse, "virtual tables may not be indexed");
                goto exit_create_index;
            }

            /*
      ** Find the name of the index.  Make sure there is not already another
      ** index or table with the same name.
      **
      ** Exception:  If we are reading the names of permanent indices from the
      ** sqlite_master table (because some other process changed the schema) and
      ** one of the index names collides with the name of a temporary table or
      ** index, then we will continue to process this index.
      **
      ** If pName==0 it means that we are
      ** dealing with a primary key or UNIQUE constraint.  We have to invent our
      ** own name.
      */
            if (pName != null)
            {
                zName = NameFromToken(db, pName);
                if (zName == null) goto exit_create_index;
                if (StatusCode.SQLITE_OK != CheckObjectName(pParse, zName))
                {
                    goto exit_create_index;
                }
                if (0 == db.init.busy)
                {
                    if (FindTable(db, zName, null) != null)
                    {
                        Utility.Sqlite3ErrorMsg(pParse, "there is already a table named %s", zName);
                        goto exit_create_index;
                    }
                }
                if (FindIndex(db, zName, pDb.zName) != null)
                {
                    if (ifNotExist == 0)
                    {
                        Utility.Sqlite3ErrorMsg(pParse, "index %s already exists", zName);
                    }
                    goto exit_create_index;
                }
            }
            else
            {
                int n = 0;
                Index pLoop;
                for (pLoop = pTab.pIndex, n = 1; pLoop != null; pLoop = pLoop.pNext, n++)
                {
                }
                zName = Print.MPrintf(db, "sqlite_autoindex_%s_%d", pTab.zName, n);
                if (zName == null)
                {
                    goto exit_create_index;
                }
            }

            /* Check for authorization to create an index.
      */
#if !SQLITE_OMIT_AUTHORIZATION
{
string zDb = pDb.zName;
if( sqlite3AuthCheck(pParse, ActionCode.SQLITE_INSERT, Helper.SchemaTable(iDb), 0, zDb) ){
goto exit_create_index;
}
i = ActionCode.SQLITE_CREATE_INDEX;
if( Const.OMIT_TEMPDB ==0&& iDb==1 ) i = ActionCode.SQLITE_CREATE_TEMP_INDEX;
if( sqlite3AuthCheck(pParse, i, zName, pTab.zName, zDb) ){
goto exit_create_index;
}
}
#endif

            /* If pList==0, it means this routine was called to make a primary
** key out of the last column added to the table under construction.
** So create a fake list to simulate this.
*/
            if (pList == null)
            {
                nullId.z = pTab.aCol[pTab.nCol - 1].zName;
                nullId.n = Utility.Sqlite3Strlen30(nullId.z);
                pList = ExprHelper.ExprListAppend(pParse, null, null);
                if (pList == null) goto exit_create_index;
                ExprHelper.ExprListSetName(pParse, pList, nullId, 0);
                pList.a[0].sortOrder = (byte) sortOrder;
            }

            /* Figure out how many bytes of space are required to store explicitly
      ** specified collation sequence names.
      */
            for (i = 0; i < pList.nExpr; i++)
            {
                Expr pExpr = pList.a[i].pExpr;
                if (pExpr != null)
                {
                    CollSeq pColl = pExpr.pColl;
                    /* Either pColl!=0 or there was an OOM failure.  But if an OOM
          ** failure we have quit before reaching this point. */
                    if (UnitTest.ALWAYS(pColl != null))
                    {
                        nExtra += (1 + Utility.Sqlite3Strlen30(pColl.zName));
                    }
                }
            }

            /*
      ** Allocate the index structure.
      */
            nName = Utility.Sqlite3Strlen30(zName);
            nCol = pList.nExpr;
            pIndex = new Index();
            // Malloc.DbMallocZero( db,
            //    Index.Length +              /* Index structure  */
            //    sizeof( int ) * nCol +           /* Index.aiColumn   */
            //    sizeof( int ) * ( nCol + 1 ) +       /* Index.aiRowEst   */
            //    sizeof( char* ) * nCol +        /* Index.azColl     */
            //    byte.Length * nCol +            /* Index.aSortOrder */
            //    nName + 1 +                  /* Index.zName      */
            //    nExtra                       /* Collation sequence names */
            //);
            //if ( db.mallocFailed != 0 )
            //{
            //  goto exit_create_index;
            //}
            pIndex.azColl = new string[nCol + 1]; //(char**)(pIndex[1]);
            pIndex.aiColumn = new int[nCol + 1]; //(int *)(pIndex->azColl[nCol]);
            pIndex.aiRowEst = new int[nCol + 1]; //(unsigned *)(pIndex->aiColumn[nCol]);
            pIndex.aSortOrder = new byte[nCol + 1]; //(byte *)(pIndex->aiRowEst[nCol+1]);
            //pIndex.zName = null;// (char*)( &pIndex->aSortOrder[nCol] );
            zExtra = new StringBuilder(nName + 1); // (char*)( &pIndex.zName[nName + 1] );
            if (zName.Length == nName) pIndex.zName = zName;
            else
            {
                pIndex.zName = zName.Substring(0, nName);
            } // memcpy( pIndex.zName, zName, nName + 1 );
            pIndex.pTable = pTab;
            pIndex.nColumn = pList.nExpr;
            pIndex.onError = (byte) onError;
            pIndex.autoIndex = (byte) (pName == null ? 1 : 0);
            pIndex.pSchema = db.aDb[iDb].pSchema;

            /* Check to see if we should honor DESC requests on index columns
      */
            if (pDb.pSchema.file_format >= 4)
            {
                sortOrderMask = 1; /* Honor DESC */
            }
            else
            {
                sortOrderMask = 0; /* Ignore DESC */
            }

            /* Scan the names of the columns of the table to be indexed and
      ** load the column indices into the Index structure.  Report an error
      ** if any column is not found.
      **
      ** TODO:  Add a test to make sure that the same column is not named
      ** more than once within the same index.  Only the first instance of
      ** the column will ever be used by the optimizer.  Note that using the
      ** same column more than once cannot be an error because that would
      ** break backwards compatibility - it needs to be a warning.
      */
            for (i = 0; i < pList.nExpr; i++)
            {
//, pListItem++){
                pListItem = pList.a[i];
                string zColName = pListItem.zName;
                Column pTabCol;
                byte requestedSortOrder;
                string zColl; /* Collation sequence name */

                for (j = 0; j < pTab.nCol; j++)
                {
//, pTabCol++){
                    pTabCol = pTab.aCol[j];
                    if (Utility.Sqlite3StrICmp(zColName, pTabCol.zName) == 0) break;
                }
                if (j >= pTab.nCol)
                {
                    Utility.Sqlite3ErrorMsg(pParse, "table %s has no column named %s",
                                    pTab.zName, zColName);
                    goto exit_create_index;
                }
                pIndex.aiColumn[i] = j;
                /* Justification of the UnitTest.ALWAYS(pListItem->pExpr->pColl):  Because of
        ** the way the "idxlist" non-terminal is constructed by the parser,
        ** if pListItem->pExpr is not null then either pListItem->pExpr->pColl
        ** must exist or else there must have been an OOM error.  But if there
        ** was an OOM error, we would never reach this point. */
                if (pListItem.pExpr != null && UnitTest.ALWAYS(pListItem.pExpr.pColl))
                {
                    int nColl;
                    zColl = pListItem.pExpr.pColl.zName;
                    nColl = Utility.Sqlite3Strlen30(zColl);
                    Debug.Assert(nExtra >= nColl);
                    zExtra = new StringBuilder(zColl.Substring(0, nColl)); // memcpy( zExtra, zColl, nColl );
                    zColl = zExtra.ToString();
                    //zExtra += nColl;
                    nExtra -= nColl;
                }
                else
                {
                    zColl = pTab.aCol[j].zColl;
                    if (zColl == null)
                    {
                        zColl = db.pDfltColl.zName;
                    }
                }
                if (0 == db.init.busy && Build.LocateCollSeq(pParse, zColl) == null)
                {
                    goto exit_create_index;
                }
                pIndex.azColl[i] = zColl;
                requestedSortOrder = (byte) ((pListItem.sortOrder & sortOrderMask) != 0 ? 1 : 0);
                pIndex.aSortOrder[i] = requestedSortOrder;
            }
            DefaultRowEst(pIndex);

            if (pTab == pParse.pNewTable)
            {
                /* This routine has been called to create an automatic index as a
        ** result of a PRIMARY KEY or UNIQUE clause on a column definition, or
        ** a PRIMARY KEY or UNIQUE clause following the column definitions.
        ** i.e. one of:
        **
        ** CREATE TABLE t(x PRIMARY KEY, y);
        ** CREATE TABLE t(x, y, UNIQUE(x, y));
        **
        ** Either way, check to see if the table already has such an index. If
        ** so, don't bother creating this one. This only applies to
        ** automatically created indices. Users can do as they wish with
        ** explicit indices.
        **
        ** Two UNIQUE or PRIMARY KEY constraints are considered equivalent
        ** (and thus suppressing the second one) even if they have different
        ** sort orders.
        **
        ** If there are different collating sequences or if the columns of
        ** the constraint occur in different orders, then the constraints are
        ** considered distinct and both result in separate indices.
        */
                Index pIdx;
                for (pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext)
                {
                    int k;
                    Debug.Assert(pIdx.onError != OnConstraintError.OE_None);
                    Debug.Assert(pIdx.autoIndex != 0);
                    Debug.Assert(pIndex.onError != OnConstraintError.OE_None);

                    if (pIdx.nColumn != pIndex.nColumn) continue;
                    for (k = 0; k < pIdx.nColumn; k++)
                    {
                        string z1;
                        string z2;
                        if (pIdx.aiColumn[k] != pIndex.aiColumn[k]) break;
                        z1 = pIdx.azColl[k];
                        z2 = pIndex.azColl[k];
                        if (z1 != z2 && Utility.Sqlite3StrICmp(z1, z2) != 0) break;
                    }
                    if (k == pIdx.nColumn)
                    {
                        if (pIdx.onError != pIndex.onError)
                        {
                            /* This constraint creates the same index as a previous
              ** constraint specified somewhere in the CREATE TABLE statement.
              ** However the ON CONFLICT clauses are different. If both this
              ** constraint and the previous equivalent constraint have explicit
              ** ON CONFLICT clauses this is an error. Otherwise, use the
              ** explicitly specified behavior for the index.
              */
                            if (!(pIdx.onError == OnConstraintError.OE_Default || pIndex.onError == OnConstraintError.OE_Default))
                            {
                                Utility.Sqlite3ErrorMsg(pParse,
                                                "conflicting ON CONFLICT clauses specified", 0);
                            }
                            if (pIdx.onError == OnConstraintError.OE_Default)
                            {
                                pIdx.onError = pIndex.onError;
                            }
                        }
                        goto exit_create_index;
                    }
                }
            }

            /* Link the new Index structure to its table and to the other
      ** in-memory database structures.
      */
            if (db.init.busy != 0)
            {
                Index p;
                p = (Index) HashHelper.HashInsert(ref pIndex.pSchema.idxHash,
                                              pIndex.zName, Utility.Sqlite3Strlen30(pIndex.zName),
                                              pIndex);
                if (p != null)
                {
                    Debug.Assert(p == pIndex); /* Malloc must have failed */
                    //        db.mallocFailed = 1;
                    goto exit_create_index;
                }
                db.flags |= Flag.SQLITE_InternChanges;
                if (pTblName != null)
                {
                    pIndex.tnum = db.init.newTnum;
                }
            }

                /* If the db.init.busy is 0 then create the index on disk.  This
          ** involves writing the index into the master table and filling in the
          ** index with the current table contents.
          **
          ** The db.init.busy is 0 when the user first enters a CREATE INDEX
          ** command.  db.init.busy is 1 when a database is opened and
          ** CREATE INDEX statements are read out of the master table.  In
          ** the latter case the index already exists on disk, which is why
          ** we don't want to recreate it.
          **
          ** If pTblName==0 it means this index is generated as a primary key
          ** or UNIQUE constraint of a CREATE TABLE statement.  Since the table
          ** has just been created, it contains no data and the index initialization
          ** step can be skipped.
          */
            else //if ( 0 == db.init.busy )
            {
                Vdbe v;
                string zStmt;
                int iMem = ++pParse.nMem;

                v = SelectHelper.GetVdbe(pParse);
                if (v == null) goto exit_create_index;


                /* Create the rootpage for the index
        */
                BeginWriteOperation(pParse, 1, iDb);
                VdbeAux.VdbeAddOp2(v, OPCode.OP_CreateIndex, iDb, iMem);

                /* Gather the complete text of the CREATE INDEX statement into
        ** the zStmt variable
        */
                if (pStart != null)
                {
                    Debug.Assert(pEnd != null);
                    /* A named index with an explicit CREATE INDEX statement */
                    zStmt = Print.MPrintf(db, "CREATE%s INDEX %.*s",
                                           onError == OnConstraintError.OE_None ? "" : " UNIQUE",
                                           pName.z.Length - pEnd.z.Length + 1,
                                           pName.z);
                }
                else
                {
                    /* An automatic index created by a PRIMARY KEY or UNIQUE constraint */
                    /* zStmt = Print.MPrintf(""); */
                    zStmt = null;
                }

                /* Add an entry in sqlite_master for this index
        */
                NestedParse(pParse,
                                   "INSERT INTO %Q.%s VALUES('index',%Q,%Q,#%d,%Q);",
                                   db.aDb[iDb].zName, Helper.SchemaTable(iDb),
                                   pIndex.zName,
                                   pTab.zName,
                                   iMem,
                                   zStmt
                    );
                MemPool.DbFree(db, ref zStmt);

                /* Fill the index with data and reparse the schema. Code an OPCode.OP_Expire
        ** to invalidate all pre-compiled statements.
        */
                if (pTblName != null)
                {
                    RefillIndex(pParse, pIndex, iMem);
                    ChangeCookie(pParse, iDb);
                    VdbeAux.VdbeAddOp4(v, OPCode.OP_ParseSchema, iDb, 0, 0,
                                      Print.MPrintf(db, "name='%q'", pIndex.zName), P4Type.P4_DYNAMIC);
                    VdbeAux.VdbeAddOp1(v, OPCode.OP_Expire, 0);
                }
            }

            /* When adding an index to the list of indices for a table, make
      ** sure all indices labeled OnConstraintError.OE_Replace come after all those labeled
      ** OnConstraintError.OE_Ignore.  This is necessary for the correct constraint check
      ** processing (in sqlite3GenerateConstraintChecks()) as part of
      ** UPDATE and INSERT statements.
      */
            if (db.init.busy != 0 || pTblName == null)
            {
                if (onError != OnConstraintError.OE_Replace || pTab.pIndex == null
                    || pTab.pIndex.onError == OnConstraintError.OE_Replace)
                {
                    pIndex.pNext = pTab.pIndex;
                    pTab.pIndex = pIndex;
                }
                else
                {
                    Index pOther = pTab.pIndex;
                    while (pOther.pNext != null && pOther.pNext.onError != OnConstraintError.OE_Replace)
                    {
                        pOther = pOther.pNext;
                    }
                    pIndex.pNext = pOther.pNext;
                    pOther.pNext = pIndex;
                }
                pRet = pIndex;
                pIndex = null;
            }

            /* Clean up before exiting */
            exit_create_index:
            if (pIndex != null)
            {
                //Malloc.sqlite3_free( ref pIndex.zColAff );
                MemPool.DbFree(db, ref pIndex);
            }
            ExprHelper.ExprListDelete(db, ref pList);
            SrcListDelete(db, ref pTblName);
            MemPool.DbFree(db, ref zName);
            return pRet;
        }

        /*
    ** Fill the Index.aiRowEst[] array with default information - information
    ** to be used when we have not run the ANALYZE command.
    **
    ** aiRowEst[0] is suppose to contain the number of elements in the index.
    ** Since we do not know, guess 1 million.  aiRowEst[1] is an estimate of the
    ** number of rows in the table that match any particular value of the
    ** first column of the index.  aiRowEst[2] is an estimate of the number
    ** of rows that match any particular combiniation of the first 2 columns
    ** of the index.  And so forth.  It must always be the case that
    *
    **           aiRowEst[N]<=aiRowEst[N-1]
    **           aiRowEst[N]>=1
    **
    ** Apart from that, we have little to go on besides intuition as to
    ** how aiRowEst[] should be initialized.  The numbers generated here
    ** are based on typical values found in actual indices.
    */

        public static void DefaultRowEst(Index pIdx)
        {
            int[] a = pIdx.aiRowEst;
            int i;
            Debug.Assert(a != null);
            a[0] = 1000000;
            for (i = pIdx.nColumn; i >= 5; i--)
            {
                a[i] = 5;
            }
            while (i >= 1)
            {
                a[i] = 11 - i;
                i--;
            }
            if (pIdx.onError != OnConstraintError.OE_None)
            {
                a[pIdx.nColumn] = 1;
            }
        }

        /*
    ** This routine will drop an existing named index.  This routine
    ** implements the DROP INDEX statement.
    */

        public static void DropIndex(Parse pParse, SrcList pName, int ifExists)
        {
            Index pIndex;
            Vdbe v;
            sqlite3 db = pParse.db;
            int iDb;

            Debug.Assert(pParse.nErr == 0); /* Never called with prior errors */
            //if ( db.mallocFailed != 0 )
            //{
            //  goto exit_drop_index;
            //}
            Debug.Assert(pName.nSrc == 1);
            if (StatusCode.SQLITE_OK != Prepare.ReadSchema(pParse))
            {
                goto exit_drop_index;
            }
            pIndex = FindIndex(db, pName.a[0].zName, pName.a[0].zDatabase);
            if (pIndex == null)
            {
                if (ifExists == 0)
                {
                    Utility.Sqlite3ErrorMsg(pParse, "no such index: %S", pName, 0);
                }
                pParse.checkSchema = 1;
                goto exit_drop_index;
            }
            if (pIndex.autoIndex != 0)
            {
                Utility.Sqlite3ErrorMsg(pParse, "index associated with UNIQUE " +
                                        "or PRIMARY KEY constraint cannot be dropped", 0);
                goto exit_drop_index;
            }
            iDb = sqlite3SchemaToIndex(db, pIndex.pSchema);
#if !SQLITE_OMIT_AUTHORIZATION
{
int code = SQLITE_DROPCode.OP_INDEX;
Table pTab = pIndex.pTable;
string zDb = db.aDb[iDb].zName;
string zTab = Helper.SchemaTable(iDb);
if( sqlite3AuthCheck(pParse, ActionCode.SQLITE_DELETE, zTab, 0, zDb) ){
goto exit_drop_index;
}
if( Const.OMIT_TEMPDB ==0&& iDb ) code = SQLITE_DROPCode.OP_TEMP_INDEX;
if( sqlite3AuthCheck(pParse, code, pIndex.zName, pTab.zName, zDb) ){
goto exit_drop_index;
}
}
#endif

            /* Generate code to remove the index and from the master table */
            v = SelectHelper.GetVdbe(pParse);
            if (v != null)
            {
                BeginWriteOperation(pParse, 1, iDb);
                NestedParse(pParse,
                                   "DELETE FROM %Q.%s WHERE name=%Q",
                                   db.aDb[iDb].zName, Helper.SchemaTable(iDb),
                                   pIndex.zName
                    );
                if (FindTable(db, "sqlite_stat1", db.aDb[iDb].zName) != null)
                {
                    NestedParse(pParse,
                                       "DELETE FROM %Q.sqlite_stat1 WHERE idx=%Q",
                                       db.aDb[iDb].zName, pIndex.zName
                        );
                }
                ChangeCookie(pParse, iDb);
                destroyRootPage(pParse, pIndex.tnum, iDb);
                VdbeAux.VdbeAddOp4(v, OPCode.OP_DropIndex, iDb, 0, 0, pIndex.zName, 0);
            }

            exit_drop_index:
            SrcListDelete(db, ref pName);
        }

        /*
    ** pArray is a pointer to an array of objects.  Each object in the
    ** array is szEntry bytes in size.  This routine allocates a new
    ** object on the end of the array.
    **
    ** pnEntry is the number of entries already in use.  pnAlloc is
    ** the previously allocated size of the array.  initSize is the
    ** suggested initial array size allocation.
    **
    ** The index of the new entry is returned in pIdx.
    **
    ** This routine returns a pointer to the array of objects.  This
    ** might be the same as the pArray parameter or it might be a different
    ** pointer if the array was resized.
    */

        public static T[] ArrayAllocate<T>(
            sqlite3 db, /* Connection to notify of malloc failures */
            T[] pArray, /* Array of objects.  Might be reallocated */
            int szEntry, /* Size of each object in the array */
            int initSize, /* Suggested initial allocation, in elements */
            ref int pnEntry, /* Number of objects currently in use */
            ref int pnAlloc, /* Current size of the allocation, in elements */
            ref int pIdx /* Write the index of a new slot here */
            ) where T : new()
        {
            //char* z;
            if (pnEntry >= pnAlloc)
            {
                //void* pNew;
                int newSize;
                newSize = (pnAlloc)*2 + initSize;
                //pNew = Malloc.DbRealloc(db, pArray, newSize * szEntry);
                //if (pNew == 0)
                //{
                //  pIdx = -1;
                //  return pArray;
                //}
                pnAlloc = newSize; //sqlite3DbMallocSize(db, pNew)/szEntry;
                //pArray = pNew;
                Array.Resize(ref pArray, newSize);
            }
            pArray[pnEntry] = new T();
            //z = (char*)pArray;
            //memset(z[*pnEntry * szEntry], 0, szEntry);
            pIdx = pnEntry;
            ++pnEntry;
            return pArray;
        }

        /*
    ** Append a new element to the given IdList.  Create a new IdList if
    ** need be.
    **
    ** A new IdList is returned, or NULL if malloc() fails.
    */
        // OVERLOADS, so I don't need to rewrite parse.c
        public static IdList IdListAppend(sqlite3 db, int null_2, Token pToken)
        {
            return IdListAppend(db, null, pToken);
        }

        public static IdList IdListAppend(sqlite3 db, IdList pList, Token pToken)
        {
            int i = 0;
            if (pList == null)
            {
                pList = new IdList(); //Malloc.DbMallocZero(db, sizeof(IdList));
                if (pList == null) return null;
                pList.nAlloc = 0;
            }
            pList.a = ArrayAllocate(
                db,
                pList.a,
                -1, //sizeof(pList.a[0]),
                5,
                ref pList.nId,
                ref pList.nAlloc,
                ref i
                );
            if (i < 0)
            {
                IdListDelete(db, ref pList);
                return null;
            }
            pList.a[i].zName = NameFromToken(db, pToken);
            return pList;
        }

        /*
    ** Delete an IdList.
    */

        public static void IdListDelete(sqlite3 db, ref IdList pList)
        {
            int i;
            if (pList == null) return;
            for (i = 0; i < pList.nId; i++)
            {
                MemPool.DbFree(db, ref pList.a[i].zName);
            }
            MemPool.DbFree(db, ref pList.a);
            MemPool.DbFree(db, ref pList);
        }

        /*
    ** Return the index in pList of the identifier named zId.  Return -1
    ** if not found.
    */

        public static int IdListIndex(IdList pList, string zName)
        {
            int i;
            if (pList == null) return -1;
            for (i = 0; i < pList.nId; i++)
            {
                if (Utility.Sqlite3StrICmp(pList.a[i].zName, zName) == 0) return i;
            }
            return -1;
        }

        /*
    ** Expand the space allocated for the given SrcList object by
    ** creating nExtra new slots beginning at iStart.  iStart is zero based.
    ** New slots are zeroed.
    **
    ** For example, suppose a SrcList initially contains two entries: A,B.
    ** To append 3 new entries onto the end, do this:
    **
    **    Build.SrcListEnlarge(db, pSrclist, 3, 2);
    **
    ** After the call above it would contain:  A, B, nil, nil, nil.
    ** If the iStart argument had been 1 instead of 2, then the result
    ** would have been:  A, nil, nil, nil, B.  To prepend the new slots,
    ** the iStart value would be 0.  The result then would
    ** be: nil, nil, nil, A, B.
    **
    ** If a memory allocation fails the SrcList is unchanged.  The
    ** db.mallocFailed flag will be set to true.
    */

        public static SrcList SrcListEnlarge(
            sqlite3 db, /* Database connection to notify of OOM errors */
            SrcList pSrc, /* The SrcList to be enlarged */
            int nExtra, /* Number of new slots to add to pSrc.a[] */
            int iStart /* Index in pSrc.a[] of first new slot */
            )
        {
            int i;

            /* Sanity checking on calling parameters */
            Debug.Assert(iStart >= 0);
            Debug.Assert(nExtra >= 1);
            Debug.Assert(pSrc != null);
            Debug.Assert(iStart <= pSrc.nSrc);

            /* Allocate additional space if needed */
            if (pSrc.nSrc + nExtra > pSrc.nAlloc)
            {
                int nAlloc = pSrc.nSrc + nExtra;
                int nGot;
                // Malloc.DbRealloc(db, pSrc,
                //     sizeof(*pSrc) + (nAlloc-1)*sizeof(pSrc.a[0]) );
                pSrc.nAlloc = (short) nAlloc;
                Array.Resize(ref pSrc.a, nAlloc);
                //    nGot = (sqlite3DbMallocSize(db, pNew) - sizeof(*pSrc))/sizeof(pSrc->a[0])+1;
                //pSrc->nAlloc = (ushort)nGot;
            }

            /* Move existing slots that come after the newly inserted slots
      ** out of the way */
            for (i = pSrc.nSrc - 1; i >= iStart; i--)
            {
                pSrc.a[i + nExtra] = pSrc.a[i];
            }
            pSrc.nSrc += (short) nExtra;

            /* Zero the newly allocated slots */
            //memset(&pSrc.a[iStart], 0, sizeof(pSrc.a[0])*nExtra);
            for (i = iStart; i < iStart + nExtra; i++)
            {
                pSrc.a[i] = new SrcList_item();
                pSrc.a[i].iCursor = -1;
            }

            /* Return a pointer to the enlarged SrcList */
            return pSrc;
        }


        /*
    ** Append a new table name to the given SrcList.  Create a new SrcList if
    ** need be.  A new entry is created in the SrcList even if pTable is NULL.
    **
    ** A SrcList is returned, or NULL if there is an OOM error.  The returned
    ** SrcList might be the same as the SrcList that was input or it might be
    ** a new one.  If an OOM error does occurs, then the prior value of pList
    ** that is input to this routine is automatically freed.
    **
    ** If pDatabase is not null, it means that the table has an optional
    ** database name prefix.  Like this:  "database.table".  The pDatabase
    ** points to the table name and the pTable points to the database name.
    ** The SrcList.a[].zName field is filled with the table name which might
    ** come from pTable (if pDatabase is NULL) or from pDatabase.
    ** SrcList.a[].zDatabase is filled with the database name from pTable,
    ** or with NULL if no database is specified.
    **
    ** In other words, if call like this:
    **
    **         Build.SrcListAppend(D,A,B,0);
    **
    ** Then B is a table name and the database name is unspecified.  If called
    ** like this:
    **
    **         Build.SrcListAppend(D,A,B,C);
    **
    ** Then C is the table name and B is the database name.  If C is defined
    ** then so is B.  In other words, we never have a case where:
    **
    **         Build.SrcListAppend(D,A,0,C);
    **
    ** Both pTable and pDatabase are assumed to be quoted.  They are dequoted
    ** before being added to the SrcList.
    */
        // OVERLOADS, so I don't need to rewrite parse.c
        public static SrcList SrcListAppend(sqlite3 db, int null_2, Token pTable, int null_4)
        {
            return SrcListAppend(db, null, pTable, null);
        }

        public static SrcList SrcListAppend(sqlite3 db, int null_2, Token pTable, Token pDatabase)
        {
            return SrcListAppend(db, null, pTable, pDatabase);
        }

        public static SrcList SrcListAppend(
            sqlite3 db, /* Connection to notify of malloc failures */
            SrcList pList, /* Append to this SrcList. NULL creates a new SrcList */
            Token pTable, /* Table to append */
            Token pDatabase /* Database of the table */
            )
        {
            SrcList_item pItem;
            Debug.Assert(pDatabase == null || pTable != null); /* Cannot have C without B */
            if (pList == null)
            {
                pList = new SrcList(); //Malloc.DbMallocZero(db, SrcList.Length );
                //if ( pList == null ) return null;
                pList.nAlloc = 1;
                pList.a = new SrcList_item[1];
            }
            pList = SrcListEnlarge(db, pList, 1, pList.nSrc);
            //if ( db.mallocFailed != 0 )
            //{
            //  Build.SrcListDelete( db, ref pList );
            //  return null;
            //}
            pItem = pList.a[pList.nSrc - 1];
            if (pDatabase != null && String.IsNullOrEmpty(pDatabase.z))
            {
                pDatabase = null;
            }
            if (pDatabase != null)
            {
                Token pTemp = pDatabase;
                pDatabase = pTable;
                pTable = pTemp;
            }
            pItem.zName = NameFromToken(db, pTable);
            pItem.zDatabase = NameFromToken(db, pDatabase);
            return pList;
        }

        /*
    ** Assign VdbeCursor index numbers to all tables in a SrcList
    */

        public static void SrcListAssignCursors(Parse pParse, SrcList pList)
        {
            int i;
            SrcList_item pItem;
            Debug.Assert(pList != null /* || pParse.db.mallocFailed != 0 */);
            if (pList != null)
            {
                for (i = 0; i < pList.nSrc; i++)
                {
                    pItem = pList.a[i];
                    if (pItem.iCursor >= 0) break;
                    pItem.iCursor = pParse.nTab++;
                    if (pItem.pSelect != null)
                    {
                        SrcListAssignCursors(pParse, pItem.pSelect.pSrc);
                    }
                }
            }
        }

        /*
    ** Delete an entire SrcList including all its substructure.
    */

        public static void SrcListDelete(sqlite3 db, ref SrcList pList)
        {
            int i;
            SrcList_item pItem;
            if (pList == null) return;
            for (i = 0; i < pList.nSrc; i++)
            {
//, pItem++){
                pItem = pList.a[i];
                MemPool.DbFree(db, ref pItem.zDatabase);
                MemPool.DbFree(db, ref pItem.zName);
                MemPool.DbFree(db, ref pItem.zAlias);
                MemPool.DbFree(db, ref pItem.zIndex);
                DeleteTable(ref pItem.pTab);
                sqlite3SelectDelete(db, ref pItem.pSelect);
                ExprHelper.ExprDelete(db, ref pItem.pOn);
                IdListDelete(db, ref pItem.pUsing);
            }
            MemPool.DbFree(db, ref pList);
        }

        /*
    ** This routine is called by the parser to add a new term to the
    ** end of a growing FROM clause.  The "p" parameter is the part of
    ** the FROM clause that has already been constructed.  "p" is NULL
    ** if this is the first term of the FROM clause.  pTable and pDatabase
    ** are the name of the table and database named in the FROM clause term.
    ** pDatabase is NULL if the database name qualifier is missing - the
    ** usual case.  If the term has a alias, then pAlias points to the
    ** alias token.  If the term is a subquery, then pSubquery is the
    ** SELECT statement that the subquery encodes.  The pTable and
    ** pDatabase parameters are NULL for subqueries.  The pOn and pUsing
    ** parameters are the content of the ON and USING clauses.
    **
    ** Return a new SrcList which encodes is the FROM with the new
    ** term added.
    */
        // OVERLOADS, so I don't need to rewrite parse.c
        public static SrcList SrcListAppendFromTerm(Parse pParse, SrcList p, int null_3, int null_4,
                                                            Token pAlias, Select pSubquery, Expr pOn, IdList pUsing)
        {
            return SrcListAppendFromTerm(pParse, p, null, null, pAlias, pSubquery, pOn, pUsing);
        }

        public static SrcList SrcListAppendFromTerm(Parse pParse, SrcList p, Token pTable, Token pDatabase,
                                                            Token pAlias, int null_6, Expr pOn, IdList pUsing)
        {
            return SrcListAppendFromTerm(pParse, p, pTable, pDatabase, pAlias, null, pOn, pUsing);
        }

        public static SrcList SrcListAppendFromTerm(
            Parse pParse, /* Parsing context */
            SrcList p, /* The left part of the FROM clause already seen */
            Token pTable, /* Name of the table to add to the FROM clause */
            Token pDatabase, /* Name of the database containing pTable */
            Token pAlias, /* The right-hand side of the AS subexpression */
            Select pSubquery, /* A subquery used in place of a table name */
            Expr pOn, /* The ON clause of a join */
            IdList pUsing /* The USING clause of a join */
            )
        {
            SrcList_item pItem;
            sqlite3 db = pParse.db;
            if (null == p && (pOn != null || pUsing != null))
            {
                Utility.Sqlite3ErrorMsg(pParse, "a JOIN clause is required before %s",
                                (pOn != null ? "ON" : "USING")
                    );
                goto append_from_error;
            }
            p = SrcListAppend(db, p, pTable, pDatabase);
            //if ( p == null || UnitTest.NEVER( p.nSrc == 0 ) )
            //{
            //  goto append_from_error;
            //}
            pItem = p.a[p.nSrc - 1];
            Debug.Assert(pAlias != null);
            if (pAlias.n != 0)
            {
                pItem.zAlias = NameFromToken(db, pAlias);
            }
            pItem.pSelect = pSubquery;
            pItem.pOn = pOn;
            pItem.pUsing = pUsing;
            return p;
            append_from_error:
            Debug.Assert(p == null);
            ExprHelper.ExprDelete(db, ref pOn);
            IdListDelete(db, ref pUsing);
            sqlite3SelectDelete(db, ref pSubquery);
            return null;
        }

        /*
    ** Add an INDEXED BY or NOT INDEXED clause to the most recently added
    ** element of the source-list passed as the second argument.
    */

        public static void SrcListIndexedBy(Parse pParse, SrcList p, Token pIndexedBy)
        {
            Debug.Assert(pIndexedBy != null);
            if (p != null && UnitTest.ALWAYS(p.nSrc > 0))
            {
                SrcList_item pItem = p.a[p.nSrc - 1];
                Debug.Assert(0 == pItem.notIndexed && pItem.zIndex == null);
                if (pIndexedBy.n == 1 && null == pIndexedBy.z)
                {
                    /* A "NOT INDEXED" clause was supplied. See parse.y
          ** construct "indexed_opt" for details. */
                    pItem.notIndexed = 1;
                }
                else
                {
                    pItem.zIndex = NameFromToken(pParse.db, pIndexedBy);
                }
            }
        }

        /*
    ** When building up a FROM clause in the parser, the join operator
    ** is initially attached to the left operand.  But the code generator
    ** expects the join operator to be on the right operand.  This routine
    ** Shifts all join operators from left to right for an entire FROM
    ** clause.
    **
    ** Example: Suppose the join is like this:
    **
    **           A natural cross join B
    **
    ** The operator is "natural cross join".  The A and B operands are stored
    ** in p.a[0] and p.a[1], respectively.  The parser initially stores the
    ** operator with A.  This routine shifts that operator over to B.
    */

        public static void SrcListShiftJoinType(SrcList p)
        {
            if (p != null && p.a != null)
            {
                int i;
                for (i = p.nSrc - 1; i > 0; i--)
                {
                    p.a[i].jointype = p.a[i - 1].jointype;
                }
                p.a[0].jointype = 0;
            }
        }

        /*
    ** Begin a transaction
    */

        public static void BeginTransaction(Parse pParse, int type)
        {
            sqlite3 db;
            Vdbe v;
            int i;

            Debug.Assert(pParse != null);
            db = pParse.db;
            Debug.Assert(db != null);
            /*  if( db.aDb[0].pBt==0 ) return; */
            if (sqlite3AuthCheck(pParse, ActionCode.SQLITE_TRANSACTION, "BEGIN", null, null) != 0)
            {
                return;
            }
            v = SelectHelper.GetVdbe(pParse);
            if (v == null) return;
            if (type != TokenKeyword.TK_DEFERRED)
            {
                for (i = 0; i < db.nDb; i++)
                {
                    VdbeAux.VdbeAddOp2(v, OPCode.OP_Transaction, i, (type == TokenKeyword.TK_EXCLUSIVE) ? 2 : 1);
                    VdbeAux.VdbeUsesBtree(v, i);
                }
            }
            VdbeAux.VdbeAddOp2(v, OPCode.OP_AutoCommit, 0, 0);
        }

        /*
    ** Commit a transaction
    */

        public static void CommitTransaction(Parse pParse)
        {
            sqlite3 db;
            Vdbe v;

            Debug.Assert(pParse != null);
            db = pParse.db;
            Debug.Assert(db != null);
            /*  if( db.aDb[0].pBt==0 ) return; */
            if (sqlite3AuthCheck(pParse, ActionCode.SQLITE_TRANSACTION, "COMMIT", null, null) != 0)
            {
                return;
            }
            v = SelectHelper.GetVdbe(pParse);
            if (v != null)
            {
                VdbeAux.VdbeAddOp2(v, OPCode.OP_AutoCommit, 1, 0);
            }
        }

        /*
    ** Rollback a transaction
    */

        public static void RollbackTransaction(Parse pParse)
        {
            sqlite3 db;
            Vdbe v;

            Debug.Assert(pParse != null);
            db = pParse.db;
            Debug.Assert(db != null);
            /*  if( db.aDb[0].pBt==0 ) return; */
            if (sqlite3AuthCheck(pParse, ActionCode.SQLITE_TRANSACTION, "ROLLBACK", null, null) != 0)
            {
                return;
            }
            v = SelectHelper.GetVdbe(pParse);
            if (v != null)
            {
                VdbeAux.VdbeAddOp2(v, OPCode.OP_AutoCommit, 1, 1);
            }
        }

        /*
    ** This function is called by the parser when it parses a command to create,
    ** release or rollback an SQL savepoint.
    */

        public static void Savepoint(Parse pParse, int op, Token pName)
        {
            string zName = NameFromToken(pParse.db, pName);
            if (zName != null)
            {
                Vdbe v = SelectHelper.GetVdbe(pParse);
#if !SQLITE_OMIT_AUTHORIZATION
byte az[] = { "BEGIN", "RELEASE", "ROLLBACK" };
Debug.Assert( !SavePoint.SAVEPOINT_BEGIN && SavePoint.SAVEPOINT_RELEASE==1 && SavePoint.SAVEPOINT_ROLLBACK==2 );
#endif
                if (null == v
#if !SQLITE_OMIT_AUTHORIZATION
|| sqlite3AuthCheck(pParse, ActionCode.SQLITE_SAVEPOINT, az[op], zName, 0)
#endif
                    )
                {
                    MemPool.DbFree(pParse.db, ref zName);
                    return;
                }
                VdbeAux.VdbeAddOp4(v, OPCode.OP_Savepoint, op, 0, 0, zName, P4Type.P4_DYNAMIC);
            }
        }

        /*
    ** Make sure the TEMP database is open and available for use.  Return
    ** the number of errors.  Leave any error messages in the pParse structure.
    */

        public static int OpenTempDatabase(Parse pParse)
        {
            sqlite3 db = pParse.db;
            if (db.aDb[1].pBt == null && pParse.explain == 0)
            {
                int rc;
                Btree pBt = null;
                const int flags =
                    FileOpenOperation.SQLITE_OPEN_READWRITE |
                    FileOpenOperation.SQLITE_OPEN_CREATE |
                    FileOpenOperation.SQLITE_OPEN_EXCLUSIVE |
                    FileOpenOperation.SQLITE_OPEN_DELETEONCLOSE |
                    FileOpenOperation.SQLITE_OPEN_TEMP_DB;

                rc = sqlite3BtreeFactory(db, null, false, Const.SQLITE_DEFAULT_CACHE_SIZE, flags, ref pBt);
                if (rc != StatusCode.SQLITE_OK)
                {
                    Utility.Sqlite3ErrorMsg(pParse, "unable to open a temporary database " +
                                            "file for storing temporary tables");
                    pParse.rc = rc;
                    return 1;
                }
                db.aDb[1].pBt = pBt;
                Debug.Assert(db.aDb[1].pSchema != null);
                if (StatusCode.SQLITE_NOMEM == sqlite3BtreeSetPageSize(pBt, db.nextPagesize, -1, 0))
                {
                    //  db.mallocFailed = 1;
                }
                sqlite3PagerJournalMode(sqlite3BtreePager(pBt), db.dfltJournalMode);
            }
            return 0;
        }

        /*
    ** Generate VDBE code that will verify the schema cookie and start
    ** a read-transaction for all named database files.
    **
    ** It is important that all schema cookies be verified and all
    ** read transactions be started before anything else happens in
    ** the VDBE program.  But this routine can be called after much other
    ** code has been generated.  So here is what we do:
    **
    ** The first time this routine is called, we code an OPCode.OP_Goto that
    ** will jump to a subroutine at the end of the program.  Then we
    ** record every database that needs its schema verified in the
    ** pParse.cookieMask field.  Later, after all other code has been
    ** generated, the subroutine that does the cookie verifications and
    ** starts the transactions will be coded and the OPCode.OP_Goto P2 value
    ** will be made to point to that subroutine.  The generation of the
    ** cookie verification subroutine code happens in Build.FinishCoding().
    **
    ** If iDb<0 then code the OPCode.OP_Goto only - don't set flag to verify the
    ** schema on any databases.  This can be used to position the OPCode.OP_Goto
    ** early in the code, before we know if any database tables will be used.
    */

        public static void CodeVerifySchema(Parse pParse, int iDb)
        {
            Parse pToplevel = sqlite3ParseToplevel(pParse);

            if (pToplevel.cookieGoto == 0)
            {
                Vdbe v = SelectHelper.GetVdbe(pToplevel);
                if (v == null) return; /* This only happens if there was a prior error */
                pToplevel.cookieGoto = VdbeAux.VdbeAddOp2(v, OPCode.OP_Goto, 0, 0) + 1;
            }
            if (iDb >= 0)
            {
                sqlite3 db = pToplevel.db;
                int mask;
                Debug.Assert(iDb < db.nDb);
                Debug.Assert(db.aDb[iDb].pBt != null || iDb == 1);
                Debug.Assert(iDb < Const.SQLITE_MAX_ATTACHED + 2);
                mask = (1 << iDb);
                if ((pToplevel.cookieMask & mask) == 0)
                {
                    pToplevel.cookieMask |= (uint) mask;
                    pToplevel.cookieValue[iDb] = db.aDb[iDb].pSchema.schema_cookie;
                    if (0 == Const.OMIT_TEMPDB && iDb == 1)
                    {
                        OpenTempDatabase(pToplevel);
                    }
                }
            }
        }

        /*
    ** Generate VDBE code that prepares for doing an operation that
    ** might change the database.
    **
    ** This routine starts a new transaction if we are not already within
    ** a transaction.  If we are already within a transaction, then a checkpoint
    ** is set if the setStatement parameter is true.  A checkpoint should
    ** be set for operations that might fail (due to a constraint) part of
    ** the way through and which will need to undo some writes without having to
    ** rollback the whole transaction.  For operations where all constraints
    ** can be checked before any changes are made to the database, it is never
    ** necessary to undo a write and the checkpoint should not be set.
    */

        public static void BeginWriteOperation(Parse pParse, int setStatement, int iDb)
        {
            Parse pToplevel = sqlite3ParseToplevel(pParse);
            CodeVerifySchema(pParse, iDb);
            pToplevel.writeMask |= (uint) (1 << iDb);
            pToplevel.isMultiWrite |= (byte) setStatement;
        }

        /*
    ** Indicate that the statement currently under construction might write
    ** more than one entry (example: deleting one row then inserting another,
    ** inserting multiple rows in a table, or inserting a row and index entries.)
    ** If an abort occurs after some of these writes have completed, then it will
    ** be necessary to undo the completed writes.
    */

        public static void MultiWrite(Parse pParse)
        {
            Parse pToplevel = sqlite3ParseToplevel(pParse);
            pToplevel.isMultiWrite = 1;
        }

        /* 
    ** The code generator calls this routine if is discovers that it is
    ** possible to abort a statement prior to completion.  In order to 
    ** perform this abort without corrupting the database, we need to make
    ** sure that the statement is protected by a statement transaction.
    **
    ** Technically, we only need to set the mayAbort flag if the
    ** isMultiWrite flag was previously set.  There is a time dependency
    ** such that the abort must occur after the multiwrite.  This makes
    ** some statements involving the REPLACE conflict resolution algorithm
    ** go a little faster.  But taking advantage of this time dependency
    ** makes it more difficult to prove that the code is correct (in 
    ** particular, it prevents us from writing an effective
    ** implementation of sqlite3AssertMayAbort()) and so we have chosen
    ** to take the safe route and skip the optimization.
    */

        public static void MayAbort(Parse pParse)
        {
            Parse pToplevel = sqlite3ParseToplevel(pParse);
            pToplevel.mayAbort = 1;
        }

        /*
    ** Code an OPCode.OP_Halt that causes the vdbe to return an StatusCode.SQLITE_CONSTRAINT
    ** error. The onError parameter determines which (if any) of the statement
    ** and/or current transaction is rolled back.
    */

        public static void HaltConstraint(Parse pParse, int onError, string p4, int p4type)
        {
            Vdbe v = SelectHelper.GetVdbe(pParse);
            if (onError == OnConstraintError.OE_Abort)
            {
                MayAbort(pParse);
            }
            VdbeAux.VdbeAddOp4(v, OPCode.OP_Halt, StatusCode.SQLITE_CONSTRAINT, onError, 0, p4, p4type);
        }

        public static void HaltConstraint(Parse pParse, int onError, byte[] p4, int p4type)
        {
            Vdbe v = SelectHelper.GetVdbe(pParse);
            if (onError == OnConstraintError.OE_Abort)
            {
                MayAbort(pParse);
            }
            VdbeAux.VdbeAddOp4(v, OPCode.OP_Halt, StatusCode.SQLITE_CONSTRAINT, onError, 0, p4, p4type);
        }

        /*
    ** Check to see if pIndex uses the collating sequence pColl.  Return
    ** true if it does and false if it does not.
    */
#if !SQLITE_OMIT_REINDEX
        private static bool collationMatch(string zColl, Index pIndex)
        {
            int i;
            Debug.Assert(zColl != null);
            for (i = 0; i < pIndex.nColumn; i++)
            {
                string z = pIndex.azColl[i];
                Debug.Assert(z != null);
                if (0 == Utility.Sqlite3StrICmp(z, zColl))
                {
                    return true;
                }
            }
            return false;
        }
#endif

        /*
** Recompute all indices of pTab that use the collating sequence pColl.
** If pColl == null then recompute all indices of pTab.
*/
#if !SQLITE_OMIT_REINDEX
        private static void reindexTable(Parse pParse, Table pTab, string zColl)
        {
            Index pIndex; /* An index associated with pTab */

            for (pIndex = pTab.pIndex; pIndex != null; pIndex = pIndex.pNext)
            {
                if (zColl == null || collationMatch(zColl, pIndex))
                {
                    int iDb = sqlite3SchemaToIndex(pParse.db, pTab.pSchema);
                    BeginWriteOperation(pParse, 0, iDb);
                    RefillIndex(pParse, pIndex, -1);
                }
            }
        }
#endif

        /*
** Recompute all indices of all tables in all databases where the
** indices use the collating sequence pColl.  If pColl == null then recompute
** all indices everywhere.
*/
#if !SQLITE_OMIT_REINDEX
        private static void reindexDatabases(Parse pParse, string zColl)
        {
            Db pDb; /* A single database */
            int iDb; /* The database index number */
            sqlite3 db = pParse.db; /* The database connection */
            HashElem k; /* For looping over tables in pDb */
            Table pTab; /* A table in the database */

            for (iDb = 0; iDb < db.nDb; iDb++) //, pDb++ )
            {
                pDb = db.aDb[iDb];
                Debug.Assert(pDb != null);
                for (k = pDb.pSchema.tblHash.first; k != null; k = k.next)
                    //for ( k = HashHelper.HashFirst( pDb.pSchema.tblHash ) ; k != null ; k = HashHelper.HashNext( k ) )
                {
                    pTab = (Table) k.data; // HashHelper.HashData( k );
                    reindexTable(pParse, pTab, zColl);
                }
            }
        }
#endif

        /*
** Generate code for the REINDEX command.
**
**        REINDEX                            -- 1
**        REINDEX  <collation>               -- 2
**        REINDEX  ?<database>.?<tablename>  -- 3
**        REINDEX  ?<database>.?<indexname>  -- 4
**
** Form 1 causes all indices in all attached databases to be rebuilt.
** Form 2 rebuilds all indices in all databases that use the named
** collating function.  Forms 3 and 4 rebuild the named index or all
** indices associated with the named table.
*/
#if !SQLITE_OMIT_REINDEX
        // OVERLOADS, so I don't need to rewrite parse.c
        public static void Reindex(Parse pParse, int null_2, int null_3)
        {
            Reindex(pParse, null, null);
        }

        public static void Reindex(Parse pParse, Token pName1, Token pName2)
        {
            CollSeq pColl; /* Collating sequence to be reindexed, or NULL */
            string z; /* Name of a table or index */
            string zDb; /* Name of the database */
            Table pTab; /* A table in the database */
            Index pIndex; /* An index associated with pTab */
            int iDb; /* The database index number */
            sqlite3 db = pParse.db; /* The database connection */
            var pObjName = new Token(); /* Name of the table or index to be reindexed */

            /* Read the database schema. If an error occurs, leave an error message
      ** and code in pParse and return NULL. */
            if (StatusCode.SQLITE_OK != Prepare.ReadSchema(pParse))
            {
                return;
            }

            if (pName1 == null)
            {
                reindexDatabases(pParse, null);
                return;
            }
            else if (UnitTest.NEVER(pName2 == null) || pName2.z == null || pName2.z.Length == 0)
            {
                string zColl;
                Debug.Assert(pName1.z != null);
                zColl = NameFromToken(pParse.db, pName1);
                if (zColl == null) return;
                pColl = Callback.FindCollSeq(db, Helper.ENC(db), zColl, 0);
                if (pColl != null)
                {
                    reindexDatabases(pParse, zColl);
                    MemPool.DbFree(db, ref zColl);
                    return;
                }
                MemPool.DbFree(db, ref zColl);
            }
            iDb = TwoPartName(pParse, pName1, pName2, ref pObjName);
            if (iDb < 0) return;
            z = NameFromToken(db, pObjName);
            if (z == null) return;
            zDb = db.aDb[iDb].zName;
            pTab = FindTable(db, z, zDb);
            if (pTab != null)
            {
                reindexTable(pParse, pTab, null);
                MemPool.DbFree(db, ref z);
                return;
            }
            pIndex = FindIndex(db, z, zDb);
            MemPool.DbFree(db, ref z);
            if (pIndex != null)
            {
                BeginWriteOperation(pParse, 0, iDb);
                RefillIndex(pParse, pIndex, -1);
                return;
            }
            Utility.Sqlite3ErrorMsg(pParse, "unable to identify the object to be reindexed");
        }
#endif

        /*
** Return a dynamicly allocated KeyInfo structure that can be used
** with OPCode.OP_OpenRead or OPCode.OP_OpenWrite to access database index pIdx.
**
** If successful, a pointer to the new structure is returned. In this case
** the caller is responsible for calling MemPool.DbFree(db, ) on the returned
** pointer. If an error occurs (out of memory or missing collation
** sequence), NULL is returned and the state of pParse updated to reflect
** the error.
*/

        public static KeyInfo IndexKeyinfo(Parse pParse, Index pIdx)
        {
            int i;
            int nCol = pIdx.nColumn;
            //int nBytes = KeyInfo.Length + (nCol - 1) * CollSeq*.Length + nCol;
            sqlite3 db = pParse.db;
            var pKey = new KeyInfo(); // (KeyInfo*)Malloc.DbMallocZero(db, nBytes);

            if (pKey != null)
            {
                pKey.db = pParse.db;
                pKey.aSortOrder = new byte[nCol];
                pKey.aColl = new CollSeq[nCol]; // (byte*)&(pKey.aColl[nCol]);
                //        Debug.Assert(pKey.aSortOrder[nCol] == &(((byte*)pKey)[nBytes]));
                for (i = 0; i < nCol; i++)
                {
                    string zColl = pIdx.azColl[i];
                    Debug.Assert(zColl != null);
                    pKey.aColl[i] = Build.LocateCollSeq(pParse, zColl);
                    pKey.aSortOrder[i] = pIdx.aSortOrder[i];
                }
                pKey.nField = (ushort) nCol;
            }

            if (pParse.nErr != 0)
            {
                pKey = null;
                MemPool.DbFree(db, ref pKey);
            }
            return pKey;
        }
    }
}